Skip to content
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

feat: Add node identity #3125

Merged
merged 39 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
7caedf3
Remove code duplication
islamaliev Oct 8, 2024
a1ea081
Assign identity to a node
islamaliev Oct 8, 2024
f916707
WIP
islamaliev Oct 8, 2024
d916c33
Return RawIdentity, add test
islamaliev Oct 10, 2024
1238aba
Fix lint
islamaliev Oct 10, 2024
1b92c70
Update docs
islamaliev Oct 10, 2024
61c8fb0
Update mocks
islamaliev Oct 10, 2024
82dc3b2
Minor refactor
islamaliev Oct 10, 2024
30a028f
PR fixup
islamaliev Oct 12, 2024
2bd361a
Polish
islamaliev Oct 12, 2024
940177d
Update mocks
islamaliev Oct 12, 2024
55413a3
PR fixup
islamaliev Oct 15, 2024
5ecf6ab
Polish
islamaliev Oct 15, 2024
a32a1f4
PR fixup
islamaliev Oct 15, 2024
62e38c5
PR fixup
islamaliev Oct 15, 2024
ed20e57
PR fixup
islamaliev Oct 17, 2024
a3396bc
Update docs
islamaliev Oct 17, 2024
3f03aa5
Rename command to node-identity
islamaliev Oct 18, 2024
5b2f935
Add assign-node-identity command
islamaliev Oct 20, 2024
b9ebd23
Update docs
islamaliev Oct 20, 2024
d158f83
Lint fix
islamaliev Oct 20, 2024
ab5dc33
Update mocks
islamaliev Oct 20, 2024
4c73fb4
Create parent command node-identity
islamaliev Oct 20, 2024
5946638
PR fixup
islamaliev Oct 21, 2024
8e39ec3
Merge remote-tracking branch 'upstream/develop' into feat/node-identity
islamaliev Oct 21, 2024
0530ab7
Make identity token updatable
islamaliev Oct 22, 2024
ab3a9ea
Update docs
islamaliev Oct 22, 2024
0e0a252
Fix lint
islamaliev Oct 22, 2024
5b3a5c4
Merge remote-tracking branch 'upstream/develop' into feat/node-identity
islamaliev Oct 22, 2024
a0f173f
Turn 2d array of identities into 1d (WIP)
islamaliev Oct 12, 2024
b6d148b
Add clear distinction between user and node identity
islamaliev Oct 13, 2024
95fc645
Pass ctx explicitly
islamaliev Oct 22, 2024
b154dbe
Remove duration from node's identity
islamaliev Oct 23, 2024
af4e2f9
Remove node-identity assign command
islamaliev Oct 23, 2024
2869d87
Polish
islamaliev Oct 24, 2024
3210fbf
Merge remote-tracking branch 'upstream/develop' into feat/node-identity
islamaliev Oct 24, 2024
e4fc548
Make identityRef optional
islamaliev Oct 25, 2024
7fc8e3d
Rename UserIdentity to ClientIdentity
islamaliev Oct 25, 2024
f3ca301
Merge remote-tracking branch 'upstream/develop' into feat/node-identity
islamaliev Oct 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 58 additions & 36 deletions acp/identity/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,49 +73,22 @@ func FromPrivateKey(
return Identity{}, err
}

var signedToken []byte
if !skipTokenGeneration {
subject := hex.EncodeToString(publicKey.SerializeCompressed())
now := time.Now()

jwtBuilder := jwt.NewBuilder()
jwtBuilder = jwtBuilder.Subject(subject)
jwtBuilder = jwtBuilder.Expiration(now.Add(duration))
jwtBuilder = jwtBuilder.NotBefore(now)
jwtBuilder = jwtBuilder.Issuer(did)
jwtBuilder = jwtBuilder.IssuedAt(now)

if audience.HasValue() {
jwtBuilder = jwtBuilder.Audience([]string{audience.Value()})
}

token, err := jwtBuilder.Build()
if err != nil {
return Identity{}, err
}

if authorizedAccount.HasValue() {
err = token.Set(acptypes.AuthorizedAccountClaim, authorizedAccount.Value())
if err != nil {
return Identity{}, err
}
}
identity := Identity{
DID: did,
PrivateKey: privateKey,
PublicKey: publicKey,
}

signedToken, err = jwt.Sign(token, jwt.WithKey(BearerTokenSignatureScheme, privateKey.ToECDSA()))
if !skipTokenGeneration {
err = identity.UpdateToken(duration, audience, authorizedAccount)
if err != nil {
return Identity{}, err
}
}

return Identity{
DID: did,
PrivateKey: privateKey,
PublicKey: publicKey,
BearerToken: string(signedToken),
}, nil
return identity, nil
}

// FromToken constructs a new `Indentity` from a bearer token.
// FromToken constructs a new `Identity` from a bearer token.
func FromToken(data []byte) (Identity, error) {
token, err := jwt.Parse(data, jwt.WithVerify(false))
if err != nil {
Expand Down Expand Up @@ -163,3 +136,52 @@ func didFromPublicKey(publicKey *secp256k1.PublicKey, producer didProducer) (str
func (identity Identity) IntoRawIdentity() RawIdentity {
return newRawIdentity(identity.PrivateKey, identity.PublicKey, identity.DID)
}

// UpdateToken updates the `BearerToken` field of the `Identity`.
//
// - duration: The [time.Duration] that this identity is valid for.
// - audience: The audience that this identity is valid for. This is required
// by the Defra http client. For example `github.com/sourcenetwork/defradb`
// - authorizedAccount: An account that this identity is authorizing to make
// SourceHub calls on behalf of this actor. This is currently required when
// using SourceHub ACP.
func (identity *Identity) UpdateToken(
duration time.Duration,
audience immutable.Option[string],
authorizedAccount immutable.Option[string],
) error {
var signedToken []byte
subject := hex.EncodeToString(identity.PublicKey.SerializeCompressed())
now := time.Now()

jwtBuilder := jwt.NewBuilder()
jwtBuilder = jwtBuilder.Subject(subject)
jwtBuilder = jwtBuilder.Expiration(now.Add(duration))
jwtBuilder = jwtBuilder.NotBefore(now)
jwtBuilder = jwtBuilder.Issuer(identity.DID)
jwtBuilder = jwtBuilder.IssuedAt(now)

if audience.HasValue() {
jwtBuilder = jwtBuilder.Audience([]string{audience.Value()})
}

token, err := jwtBuilder.Build()
if err != nil {
return err
}

if authorizedAccount.HasValue() {
err = token.Set(acptypes.AuthorizedAccountClaim, authorizedAccount.Value())
if err != nil {
return err
}
}

signedToken, err = jwt.Sign(token, jwt.WithKey(BearerTokenSignatureScheme, identity.PrivateKey.ToECDSA()))
if err != nil {
return err
}

identity.BearerToken = string(signedToken)
return nil
}
18 changes: 0 additions & 18 deletions acp/identity/raw_identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,3 @@ func (r RawIdentity) IntoIdentity() (Identity, error) {
DID: r.DID,
}, nil
}

// IntoIdentity converts a PublicRawIdentity into an Identity.
func (r PublicRawIdentity) IntoIdentity() (Identity, error) {
publicKeyBytes, err := hex.DecodeString(r.PublicKey)
if err != nil {
return Identity{}, err
}

publicKey, err := secp256k1.ParsePubKey(publicKeyBytes)
if err != nil {
return Identity{}, err
}

return Identity{
PublicKey: publicKey,
DID: r.DID,
}, nil
}
13 changes: 13 additions & 0 deletions cli/node_identity_assign.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ Example to assign an identity to the node:
if err != nil {
return err
}

if !cfg.GetBool("keyring.disabled") {
kr, err := openKeyring(cmd)
if err != nil {
return err
}

err = kr.Set(nodeIdentityKeyName, data)
if err != nil {
return err
}
}

return db.AssignNodeIdentity(cmd.Context(), identity)
},
}
Expand Down
27 changes: 0 additions & 27 deletions http/handler_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"github.com/getkin/kin-openapi/openapi3"
"github.com/sourcenetwork/immutable"

"github.com/sourcenetwork/defradb/acp/identity"
"github.com/sourcenetwork/defradb/client"
)

Expand Down Expand Up @@ -354,23 +353,6 @@ func (s *storeHandler) GetNodeIdentity(rw http.ResponseWriter, req *http.Request
responseJSON(rw, http.StatusOK, identity)
}

func (s *storeHandler) AssignNodeIdentity(rw http.ResponseWriter, req *http.Request) {
db := req.Context().Value(dbContextKey).(client.DB)

ident := identity.FromContext(req.Context())

if !ident.HasValue() {
responseJSON(rw, http.StatusBadRequest, errorResponse{ErrMissingIdentity})
return
}

err := db.AssignNodeIdentity(req.Context(), ident.Value())
if err != nil {
responseJSON(rw, http.StatusBadRequest, errorResponse{err})
return
}
}

func (h *storeHandler) bindRoutes(router *Router) {
successResponse := &openapi3.ResponseRef{
Ref: "#/components/responses/success",
Expand Down Expand Up @@ -672,14 +654,6 @@ func (h *storeHandler) bindRoutes(router *Router) {
nodeIdentity.AddResponse(200, identityResponse)
nodeIdentity.Responses.Set("400", errorResponse)

assignNodeIdentity := openapi3.NewOperation()
assignNodeIdentity.OperationID = "assign_node_identity"
assignNodeIdentity.Description = "Assign node's identity"
assignNodeIdentity.Tags = []string{"node", "identity"}
assignNodeIdentity.Responses = openapi3.NewResponses()
assignNodeIdentity.Responses.Set("200", successResponse)
assignNodeIdentity.Responses.Set("400", errorResponse)

router.AddRoute("/backup/export", http.MethodPost, backupExport, h.BasicExport)
router.AddRoute("/backup/import", http.MethodPost, backupImport, h.BasicImport)
router.AddRoute("/collections", http.MethodGet, collectionDescribe, h.GetCollection)
Expand All @@ -695,5 +669,4 @@ func (h *storeHandler) bindRoutes(router *Router) {
router.AddRoute("/schema/default", http.MethodPost, setActiveSchemaVersion, h.SetActiveSchemaVersion)
router.AddRoute("/lens", http.MethodPost, setMigration, h.SetMigration)
router.AddRoute("/node/identity", http.MethodGet, nodeIdentity, h.GetNodeIdentity)
router.AddRoute("/node/identity", http.MethodPost, assignNodeIdentity, h.AssignNodeIdentity)
}
2 changes: 1 addition & 1 deletion tests/clients/http/wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,5 +288,5 @@ func (w *Wrapper) GetNodeIdentity(ctx context.Context) (immutable.Option[identit
}

func (w *Wrapper) AssignNodeIdentity(ctx context.Context, ident identity.Identity) error {
return w.client.AssignNodeIdentity(ctx, ident)
return w.node.DB.AssignNodeIdentity(ctx, ident)
}
2 changes: 1 addition & 1 deletion tests/integration/node/identity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package encryption
package node

import (
"testing"
Expand Down
18 changes: 9 additions & 9 deletions tests/integration/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,15 +167,15 @@ func ExecuteTestCase(
skipIfViewCacheTypeUnsupported(t, testCase.SupportedViewTypes)

var clients []ClientType
//if httpClient {
clients = append(clients, HTTPClientType)
//}
//if goClient {
clients = append(clients, GoClientType)
//}
//if cliClient {
clients = append(clients, CLIClientType)
//}
if httpClient {
clients = append(clients, HTTPClientType)
}
if goClient {
clients = append(clients, GoClientType)
}
if cliClient {
clients = append(clients, CLIClientType)
}

var databases []DatabaseType
if badgerInMemory {
Expand Down