-
Notifications
You must be signed in to change notification settings - Fork 24
added baseline funcs from old repo #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
18726b1
added baseline funcs from old repo
woutslakhorst e19fe9e
added todo
woutslakhorst 1885c56
some cleanup in crypto
woutslakhorst 7d93bed
removed generateKeys from API, removed Client interface
woutslakhorst 25ba805
renamed crypto interface to KeyStore
woutslakhorst 9e7ce43
removed nuts-go-test dep
woutslakhorst 1fb6380
removed obsolete jwt lib
woutslakhorst 69abcf2
gofmt
woutslakhorst 75d02de
cleanup
woutslakhorst a2c2494
cleanup
woutslakhorst 784ae99
api test cleanup
woutslakhorst 4f04b8b
some more coverage
woutslakhorst 600f432
added coverage, added NamingFunc on key generation
woutslakhorst 86989f5
removed kid fingwerprint
woutslakhorst 11bd968
ignore mock in codeclimate
woutslakhorst 9f3783f
cleanup
woutslakhorst c76cddf
codeclimate conf
woutslakhorst b9788f1
more cleanup
woutslakhorst d544c98
remove storage package comment
woutslakhorst 6ec07c7
extra test for crypto engine
woutslakhorst 9b6c54e
one more test
woutslakhorst 893cf56
one more test
woutslakhorst 8e12154
return kid from keyStore.New
woutslakhorst 6920e63
one more test
woutslakhorst 0a10ec3
one more test
woutslakhorst 43be5e5
PR review
woutslakhorst 91c8273
one more PR thingy
woutslakhorst File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| /* | ||
| * Nuts crypto | ||
| * Copyright (C) 2019. Nuts community | ||
| * | ||
| * This program is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License as published by | ||
| * the Free Software Foundation, either version 3 of the License, or | ||
| * (at your option) any later version. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| * GNU General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License | ||
| * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| package v1 | ||
|
|
||
| import ( | ||
| "errors" | ||
| "mime" | ||
| "net/http" | ||
|
|
||
| "github.com/nuts-foundation/nuts-node/crypto" | ||
| "github.com/nuts-foundation/nuts-node/crypto/log" | ||
| "github.com/nuts-foundation/nuts-node/crypto/util" | ||
|
|
||
| "github.com/labstack/echo/v4" | ||
| "github.com/lestrrat-go/jwx/jwk" | ||
| "github.com/nuts-foundation/nuts-node/crypto/storage" | ||
| ) | ||
|
|
||
| // Wrapper implements the generated interface from oapi-codegen | ||
| type Wrapper struct { | ||
| C crypto.KeyStore | ||
| } | ||
|
|
||
| func (signRequest SignJwtRequest) validate() error { | ||
| if len(signRequest.Kid) == 0 { | ||
| return errors.New("missing kid") | ||
| } | ||
|
|
||
| if len(signRequest.Claims) == 0 { | ||
| return errors.New("missing claims") | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // SignJwt handles api calls for signing a Jwt | ||
| func (w *Wrapper) SignJwt(ctx echo.Context) error { | ||
| var signRequest = &SignJwtRequest{} | ||
| err := ctx.Bind(signRequest) | ||
| if err != nil { | ||
| log.Logger().Error(err.Error()) | ||
| return echo.NewHTTPError(http.StatusBadRequest, err.Error()) | ||
| } | ||
|
|
||
| if err := signRequest.validate(); err != nil { | ||
| return echo.NewHTTPError(http.StatusBadRequest, err) | ||
| } | ||
|
|
||
| sig, err := w.C.SignJWT(signRequest.Claims, signRequest.Kid) | ||
|
|
||
| if err != nil { | ||
| log.Logger().Error(err.Error()) | ||
| return echo.NewHTTPError(http.StatusBadRequest, err.Error()) | ||
| } | ||
|
|
||
| return ctx.String(http.StatusOK, sig) | ||
| } | ||
|
|
||
| // PublicKey returns a public key for the given kid. The urn represents a legal entity. The api returns the public key either in PEM or JWK format. | ||
| // It uses the accept header to determine this. Default is PEM (text/plain), only when application/json is requested will it return JWK. | ||
| func (w *Wrapper) PublicKey(ctx echo.Context, kid string) error { | ||
| acceptHeader := ctx.Request().Header.Get("Accept") | ||
|
|
||
| pubKey, err := w.C.GetPublicKey(kid) | ||
| if err != nil { | ||
| if errors.Is(err, storage.ErrNotFound) { | ||
| return ctx.NoContent(404) | ||
| } | ||
| log.Logger().Error(err.Error()) | ||
| return err | ||
| } | ||
|
|
||
| // starts with so we can ignore any + | ||
| if ct, _, _ := mime.ParseMediaType(acceptHeader); ct == "application/json" { | ||
| jwk, err := jwk.New(pubKey) | ||
| if err != nil { | ||
| log.Logger().Error(err.Error()) | ||
| return err | ||
| } | ||
|
|
||
| return ctx.JSON(http.StatusOK, jwk) | ||
| } | ||
|
|
||
| // backwards compatible PEM format is the default | ||
| pub, err := util.PublicKeyToPem(pubKey) | ||
| if err != nil { | ||
| log.Logger().Error(err.Error()) | ||
| return err | ||
| } | ||
|
|
||
| return ctx.String(http.StatusOK, pub) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| /* | ||
| * Nuts crypto | ||
| * Copyright (C) 2019. Nuts community | ||
| * | ||
| * This program is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License as published by | ||
| * the Free Software Foundation, either version 3 of the License, or | ||
| * (at your option) any later version. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| * GNU General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License | ||
| * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| package v1 | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "errors" | ||
| "net/http" | ||
| "testing" | ||
|
|
||
| "github.com/golang/mock/gomock" | ||
| mock2 "github.com/nuts-foundation/nuts-node/crypto/mock" | ||
| "github.com/nuts-foundation/nuts-node/crypto/storage" | ||
| "github.com/nuts-foundation/nuts-node/crypto/test" | ||
| "github.com/nuts-foundation/nuts-node/mock" | ||
| "github.com/stretchr/testify/assert" | ||
| ) | ||
|
|
||
| func TestWrapper_SignJwt(t *testing.T) { | ||
| t.Run("Missing claims returns 400", func(t *testing.T) { | ||
| ctx := newMockContext(t) | ||
| defer ctx.ctrl.Finish() | ||
|
|
||
| jsonRequest := SignJwtRequest{ | ||
| Kid: "kid", | ||
| } | ||
| jsonData, _ := json.Marshal(jsonRequest) | ||
|
|
||
| ctx.echo.EXPECT().Bind(gomock.Any()).Do(func(f interface{}) { | ||
| _ = json.Unmarshal(jsonData, f) | ||
| }) | ||
|
|
||
| err := ctx.client.SignJwt(ctx.echo) | ||
|
|
||
| assert.NotNil(t, err) | ||
| assert.Contains(t, err.Error(), "code=400, message=missing claims") | ||
| }) | ||
|
|
||
| t.Run("Missing kid returns 400", func(t *testing.T) { | ||
| ctx := newMockContext(t) | ||
| defer ctx.ctrl.Finish() | ||
|
|
||
| jsonRequest := SignJwtRequest{ | ||
| Claims: map[string]interface{}{"iss": "nuts"}, | ||
| } | ||
| jsonData, _ := json.Marshal(jsonRequest) | ||
|
|
||
| ctx.echo.EXPECT().Bind(gomock.Any()).Do(func(f interface{}) { | ||
| _ = json.Unmarshal(jsonData, f) | ||
| }) | ||
|
|
||
| err := ctx.client.SignJwt(ctx.echo) | ||
|
|
||
| assert.NotNil(t, err) | ||
| assert.Contains(t, err.Error(), "code=400, message=missing kid") | ||
| }) | ||
|
|
||
| t.Run("Sign error returns 400", func(t *testing.T) { | ||
| ctx := newMockContext(t) | ||
| defer ctx.ctrl.Finish() | ||
|
|
||
| jsonRequest := SignJwtRequest{ | ||
| Kid: "unknown", | ||
| Claims: map[string]interface{}{"iss": "nuts"}, | ||
| } | ||
| jsonData, _ := json.Marshal(jsonRequest) | ||
|
|
||
| ctx.echo.EXPECT().Bind(gomock.Any()).Do(func(f interface{}) { | ||
| _ = json.Unmarshal(jsonData, f) | ||
| }) | ||
| ctx.keyStore.EXPECT().SignJWT(gomock.Any(), "unknown").Return("", errors.New("b00m!")) | ||
|
|
||
| err := ctx.client.SignJwt(ctx.echo) | ||
|
|
||
| assert.NotNil(t, err) | ||
| assert.Contains(t, err.Error(), "code=400, message=b00m!") | ||
| }) | ||
|
|
||
| t.Run("All OK returns 200", func(t *testing.T) { | ||
| ctx := newMockContext(t) | ||
| defer ctx.ctrl.Finish() | ||
|
|
||
| jsonRequest := SignJwtRequest{ | ||
| Kid: "kid", | ||
| Claims: map[string]interface{}{"iss": "nuts"}, | ||
| } | ||
|
|
||
| jsonData, _ := json.Marshal(jsonRequest) | ||
|
|
||
| ctx.echo.EXPECT().Bind(gomock.Any()).Do(func(f interface{}) { | ||
| _ = json.Unmarshal(jsonData, f) | ||
| }) | ||
| ctx.keyStore.EXPECT().SignJWT(gomock.Any(), "kid").Return("token", nil) | ||
| ctx.echo.EXPECT().String(http.StatusOK, "token") | ||
|
|
||
| err := ctx.client.SignJwt(ctx.echo) | ||
|
|
||
| assert.Nil(t, err) | ||
| }) | ||
|
|
||
| t.Run("Missing body gives 400", func(t *testing.T) { | ||
| ctx := newMockContext(t) | ||
| defer ctx.ctrl.Finish() | ||
|
|
||
| ctx.echo.EXPECT().Bind(gomock.Any()).Return(errors.New("missing body in request")) | ||
|
|
||
| err := ctx.client.SignJwt(ctx.echo) | ||
|
|
||
| assert.NotNil(t, err) | ||
| assert.Contains(t, err.Error(), "code=400, message=missing body in request") | ||
| }) | ||
| } | ||
|
|
||
| func TestWrapper_PublicKey(t *testing.T) { | ||
| t.Run("PublicKey API call returns 200", func(t *testing.T) { | ||
| ctx := newMockContext(t) | ||
| defer ctx.ctrl.Finish() | ||
|
|
||
| key := test.GenerateECKey() | ||
|
|
||
| ctx.echo.EXPECT().Request().Return(&http.Request{}) | ||
| ctx.keyStore.EXPECT().GetPublicKey("kid").Return(key.Public(), nil) | ||
| ctx.echo.EXPECT().String(http.StatusOK, gomock.Any()) | ||
|
|
||
| _ = ctx.client.PublicKey(ctx.echo, "kid") | ||
| }) | ||
|
|
||
| t.Run("PublicKey API call returns JWK", func(t *testing.T) { | ||
| ctx := newMockContext(t) | ||
| defer ctx.ctrl.Finish() | ||
|
|
||
| key := test.GenerateECKey() | ||
|
|
||
| ctx.echo.EXPECT().Request().Return(&http.Request{Header: http.Header{"Accept": []string{"application/json"}}}) | ||
| ctx.keyStore.EXPECT().GetPublicKey("kid").Return(key.Public(), nil) | ||
| ctx.echo.EXPECT().JSON(http.StatusOK, gomock.Any()) | ||
|
|
||
| _ = ctx.client.PublicKey(ctx.echo, "kid") | ||
| }) | ||
|
|
||
| t.Run("PublicKey API call returns 404 for unknown", func(t *testing.T) { | ||
| ctx := newMockContext(t) | ||
| defer ctx.ctrl.Finish() | ||
|
|
||
| ctx.echo.EXPECT().Request().Return(&http.Request{}) | ||
| ctx.keyStore.EXPECT().GetPublicKey("kid").Return(nil, storage.ErrNotFound) | ||
| ctx.echo.EXPECT().NoContent(http.StatusNotFound) | ||
|
|
||
| _ = ctx.client.PublicKey(ctx.echo, "kid") | ||
| }) | ||
|
|
||
| t.Run("PublicKey API call returns 500 for other error", func(t *testing.T) { | ||
| ctx := newMockContext(t) | ||
| defer ctx.ctrl.Finish() | ||
|
|
||
| ctx.echo.EXPECT().Request().Return(&http.Request{Header: http.Header{"Accept": []string{"application/json"}}}) | ||
| ctx.keyStore.EXPECT().GetPublicKey("kid").Return(nil, errors.New("b00m!")) | ||
|
|
||
| err := ctx.client.PublicKey(ctx.echo, "kid") | ||
| assert.Error(t, err) | ||
| }) | ||
| } | ||
|
|
||
| type mockContext struct { | ||
| ctrl *gomock.Controller | ||
| echo *mock.MockContext | ||
| keyStore *mock2.MockKeyStore | ||
| client *Wrapper | ||
| } | ||
|
|
||
| func newMockContext(t *testing.T) mockContext { | ||
| ctrl := gomock.NewController(t) | ||
| keyStore := mock2.NewMockKeyStore(ctrl) | ||
| client := &Wrapper{C: keyStore} | ||
|
|
||
| return mockContext{ | ||
| ctrl: ctrl, | ||
| echo: mock.NewMockContext(ctrl), | ||
| keyStore: keyStore, | ||
| client: client, | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.