From e011a65744f06e2087ca282b40de7d6ad0dce5ad Mon Sep 17 00:00:00 2001 From: Eli Fabens Date: Wed, 28 Apr 2021 14:01:11 -0700 Subject: [PATCH] Add ToznySDKV3 config storage --- client.go | 32 ++++++++++-- config.go | 20 +++++--- example_client_test.go | 108 ++++++++++++++++++++--------------------- go.mod | 6 +-- go.sum | 4 +- identity.go | 1 - identity_test.go | 10 ++-- 7 files changed, 104 insertions(+), 77 deletions(-) diff --git a/client.go b/client.go index e97e3d2..ec16994 100644 --- a/client.go +++ b/client.go @@ -659,6 +659,7 @@ type ToznySDKV3 struct { APIEndpoint string // Tozny server defined globally unique id for this Client. ClientID string + config e3dbClients.ClientConfig } // ToznySDKConfig wraps parameters needed to configure a ToznySDK @@ -686,6 +687,7 @@ func NewToznySDKV3(config ToznySDKConfig) (*ToznySDKV3, error) { AccountPassword: config.AccountPassword, APIEndpoint: config.APIEndpoint, ClientID: config.ClientID, + config: config.ClientConfig, }, nil } @@ -738,16 +740,15 @@ type LoginActionData = map[string]string type IdentitySessionIntermediateResponse = identityClient.IdentitySessionRequestResponse +// TozIDLoginRequest is used to login to a TozID account to get a ToznySDKV3 or active TozID session (future plan) type TozIDLoginRequest struct { Username string Password string RealmName string - - APIBaseURL string + APIBaseURL string LoginHandler func(response *IdentitySessionIntermediateResponse) (LoginActionData, error) } - //GetSDKV3ForTozIDUser logs in a TozID user and returns the storage client of that user as a ToznySDKV3 func GetSDKV3ForTozIDUser(login TozIDLoginRequest) (*ToznySDKV3, error) { if login.APIBaseURL == "" { @@ -765,7 +766,7 @@ func GetSDKV3ForTozIDUser(login TozIDLoginRequest) (*ToznySDKV3, error) { realmInfo, err := anonymousClient.RealmInfo(ctx, login.RealmName) if err != nil { // TODO: better error message for failure to get realmInfo - return nil, err + return nil, fmt.Errorf("GetSDKV3ForTozIDUser: failed to get realm infor with error %w", err) } noteName, encryptionKeys, signingKeys, err := e3dbClients.DeriveIdentityCredentials(username, login.Password, realmInfo.Name, "") if err != nil { @@ -831,10 +832,10 @@ func GetSDKV3ForTozIDUser(login TozIDLoginRequest) (*ToznySDKV3, error) { reader = &buf } request, err := http.NewRequest("POST", sessionResponse.ActionURL, reader) - request.Header.Set("Content-Type", sessionResponse.ContentType) if err != nil { return nil, err } + request.Header.Set("Content-Type", sessionResponse.ContentType) err = e3dbClients.MakeSignedServiceCall(ctx, &http.Client{}, request, signingKeys, "", &sessionResponse) if err != nil { return nil, err @@ -925,6 +926,27 @@ type ClientConfig struct { PrivateSigningKey string `json:"private_signing_key"` } +// StoreConfigFile stores a ToznySDKV3 config file at the specified path, returning an error if any +func (c *ToznySDKV3) StoreConfigFile(path string) error { + config := ToznySDKJSONConfig{ + ConfigFile: ConfigFile{ + Version: 2, + APIBaseURL: c.APIEndpoint, + APIKeyID: c.config.APIKey, + APISecret: c.config.APISecret, + ClientID: c.config.ClientID, + ClientEmail: "", + PublicKey: c.config.EncryptionKeys.Public.Material, + PrivateKey: c.config.EncryptionKeys.Private.Material, + }, + PublicSigningKey: c.config.SigningKeys.Public.Material, + PrivateSigningKey: c.config.SigningKeys.Private.Material, + AccountUsername: c.AccountUsername, + AccountPassword: c.AccountPassword, + } + return saveJson(path, config) +} + // Register attempts to create a valid TozStore account returning the root client config for the created account and error (if any). func (c *ToznySDKV3) Register(ctx context.Context, name string, email string, password string, apiURL string) (RegisterAccountResponse, error) { if apiURL == "" { diff --git a/config.go b/config.go index b352611..c53aa01 100644 --- a/config.go +++ b/config.go @@ -109,7 +109,7 @@ func loadConfig(configPath string) (*ClientOpts, error) { }, nil } -func saveConfig(configPath string, opts *ClientOpts) error { +func saveJson(configPath string, obj interface{}) error { configFullPath, err := homedir.Expand(configPath) if err != nil { return err @@ -126,6 +126,14 @@ func saveConfig(configPath string, opts *ClientOpts) error { } defer configFd.Close() + if err = json.NewEncoder(configFd).Encode(&obj); err != nil { + return err + } + + return nil +} + +func saveConfig(configPath string, opts *ClientOpts) error { configObj := configFile{ Version: 1, ClientID: opts.ClientID, @@ -137,11 +145,7 @@ func saveConfig(configPath string, opts *ClientOpts) error { PrivateKey: encodePrivateKey(opts.PrivateKey), } - if err = json.NewEncoder(configFd).Encode(&configObj); err != nil { - return err - } - - return nil + return saveJson(configPath, configObj) } func fileExists(name string) (bool, error) { @@ -214,3 +218,7 @@ func LoadConfigFile(configPath string) (ToznySDKJSONConfig, error) { } return config, nil } + +func StoreConfigFile(configPath string, config ToznySDKJSONConfig) error { + return saveJson(configPath, config) +} diff --git a/example_client_test.go b/example_client_test.go index 9fa10d4..7dadcfa 100644 --- a/example_client_test.go +++ b/example_client_test.go @@ -4,77 +4,77 @@ package e3db_test // this code, it will share text with Tozny in an end-to-end encrypted manner. import ( - "context" - "fmt" - "github.com/tozny/e3db-go/v2" - "log" + "context" + "fmt" + "github.com/tozny/e3db-go/v2" + "log" ) func chk(err error) { - if err != nil { - log.Fatal(err) - } + if err != nil { + log.Fatal(err) + } } func printRecords(recordType string) { - client, err := e3db.GetDefaultClient() - chk(err) + client, err := e3db.GetDefaultClient() + chk(err) - // Query for all records and print them out - query := e3db.Q{} // queries all records - if recordType != "" { - query = e3db.Q{ContentTypes: []string{recordType}} - } - cursor := client.Query(context.Background(), query) - for { - record, err := cursor.Next() - if err == e3db.Done { - break - } else if err != nil { - chk(err) - } - fmt.Println("\t" + record.Meta.RecordID + " " + record.Meta.Type) - } + // Query for all records and print them out + query := e3db.Q{} // queries all records + if recordType != "" { + query = e3db.Q{ContentTypes: []string{recordType}} + } + cursor := client.Query(context.Background(), query) + for { + record, err := cursor.Next() + if err == e3db.Done { + break + } else if err != nil { + chk(err) + } + fmt.Println("\t" + record.Meta.RecordID + " " + record.Meta.Type) + } } func Example() { - // Accessing the default profile. - // You must run e3db RegisterClient before this will work: - client, err := e3db.GetDefaultClient() - chk(err) + // Accessing the default profile. + // You must run e3db RegisterClient before this will work: + client, err := e3db.GetDefaultClient() + chk(err) - fmt.Println("Current list of records:") - printRecords("") + fmt.Println("Current list of records:") + printRecords("") - // Create a new "feedback" record; this is the type the CLI uses - feedbackData := make(map[string]string) - feedbackData["comment"] = "This is some example feedback!" - feedbackData["interface"] = "Go Example Code" - record, err := client.Write(context.Background(), "feedback", feedbackData, nil) - chk(err) + // Create a new "feedback" record; this is the type the CLI uses + feedbackData := make(map[string]string) + feedbackData["comment"] = "This is some example feedback!" + feedbackData["interface"] = "Go Example Code" + record, err := client.Write(context.Background(), "feedback", feedbackData, nil) + chk(err) - // Read back the feedback we just put into the database - newFeedbackRecord, err := client.Read(context.Background(), record.Meta.RecordID) - chk(err) - fmt.Println("Read record id " + record.Meta.RecordID + ": " + newFeedbackRecord.Data["comment"]) + // Read back the feedback we just put into the database + newFeedbackRecord, err := client.Read(context.Background(), record.Meta.RecordID) + chk(err) + fmt.Println("Read record id " + record.Meta.RecordID + ": " + newFeedbackRecord.Data["comment"]) - // Fetch the Tozny feedback email address public key and client ID - feedbackClient, err := client.GetClientInfo(context.Background(), "db1744b9-3fb6-4458-a291-0bc677dba08b") - chk(err) + // Fetch the Tozny feedback email address public key and client ID + feedbackClient, err := client.GetClientInfo(context.Background(), "db1744b9-3fb6-4458-a291-0bc677dba08b") + chk(err) - // Share all "feedback" records with that user ID. - err = client.Share(context.Background(), "feedback", feedbackClient.ClientID) - chk(err) + // Share all "feedback" records with that user ID. + err = client.Share(context.Background(), "feedback", feedbackClient.ClientID) + chk(err) - fmt.Println("Current list of records after adding:") - printRecords("feedback") + fmt.Println("Current list of records after adding:") + printRecords("feedback") - // Delete the record we just created to keep things tidy. - // Comment out this line if you want to keep it - err = client.Delete(context.Background(), record.Meta.RecordID, record.Meta.Version) - chk(err) + // Delete the record we just created to keep things tidy. + // Comment out this line if you want to keep it + err = client.Delete(context.Background(), record.Meta.RecordID, record.Meta.Version) + chk(err) - fmt.Println("Current list of records after deleting:") - printRecords("feedback") + fmt.Println("Current list of records after deleting:") + printRecords("feedback") } diff --git a/go.mod b/go.mod index 1f6ecd3..d97bd16 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,13 @@ module github.com/tozny/e3db-go/v2 -go 1.13 +go 1.15 require ( github.com/google/uuid v1.1.0 github.com/jawher/mow.cli v1.0.4 github.com/mitchellh/go-homedir v1.0.0 github.com/stretchr/testify v1.6.1 // indirect - github.com/tozny/e3db-clients-go v0.0.144-0.20210428154208-cc5c7c2fe4ee + github.com/tozny/e3db-clients-go v0.0.144 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 ) - -replace github.com/tozny/e3db-clients-go => ../e3db-clients-go diff --git a/go.sum b/go.sum index 706bfab..94b05b0 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tozny/e3db-clients-go v0.0.143 h1:RCCN+fFoKOIwLasxT3JGnq8Ceczk64/ZvImEsWFclfA= -github.com/tozny/e3db-clients-go v0.0.143/go.mod h1:xqnK5S5r0qLrKCUms5Mi/3oij2ppNg2lk/8iggyn7IQ= +github.com/tozny/e3db-clients-go v0.0.144 h1:Y4j/fYRZ+KAZzMPnkEhF8QLipGPCBUUDMNK8712y3J4= +github.com/tozny/e3db-clients-go v0.0.144/go.mod h1:xqnK5S5r0qLrKCUms5Mi/3oij2ppNg2lk/8iggyn7IQ= github.com/tozny/utils-go v0.0.35 h1:gPvhlQ8QCoLBUjIx1COfYy6o4dfSM8Lrh+2FV9Ask+g= github.com/tozny/utils-go v0.0.35/go.mod h1:SHi9wnpPEEzAxbwcBhRd+jW32r+gY6S+AcWweuGytRw= golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/identity.go b/identity.go index 5ca379c..d74263d 100644 --- a/identity.go +++ b/identity.go @@ -162,7 +162,6 @@ func (i *Identity) DeriveCredentails(password string, nameSalt string) (string, return e3dbClients.DeriveIdentityCredentials(i.Username, password, i.Realm.Name, nameSalt) } - func (i *Identity) writePasswordNote(password string) (*storageClient.Note, error) { info, err := i.Realm.Info() if err != nil { diff --git a/identity_test.go b/identity_test.go index 0798097..499cfb1 100644 --- a/identity_test.go +++ b/identity_test.go @@ -7,10 +7,10 @@ import ( func TestToznySDKV3_Login(t *testing.T) { request := TozIDLoginRequest{ - Username: "", - Password: "", - RealmName: "", - APIBaseURL: "https://api.e3db.com", + Username: "", + Password: "", + RealmName: "", + APIBaseURL: "https://api.e3db.com", LoginHandler: mfaHandler, } sdk, err := GetSDKV3ForTozIDUser(request) @@ -27,4 +27,4 @@ func mfaHandler(sessionResponse *IdentitySessionIntermediateResponse) (LoginActi return totpValue, nil } return nil, fmt.Errorf("mfaHandler cannot support \"%s\" action types", sessionResponse.LoginActionType) -} \ No newline at end of file +}