M README.md => README.md +3 -2
@@ 8,6 8,7 @@ Depending on what version of kilovolt you need to interface with, you'll need to
| Protocol | Go URL |
| -------- | ---------------------------------------------------- |
-| `v3` | `go get github.com/strimertul/kilovolt-client-go` |
-| `v4` | `go get github.com/strimertul/kilovolt-client-go/v2` |
+| `v6` | `go get github.com/strimertul/kilovolt-client-go/v6` |
| `v5` | `go get github.com/strimertul/kilovolt-client-go/v3` |
+| `v4` | `go get github.com/strimertul/kilovolt-client-go/v2` |
+| `v3` | `go get github.com/strimertul/kilovolt-client-go` |
M client.go => client.go +59 -10
@@ 1,6 1,9 @@
package kvclient
import (
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/base64"
"errors"
"fmt"
"math/rand"
@@ 14,11 17,10 @@ import (
cmap "github.com/orcaman/concurrent-map"
"github.com/sirupsen/logrus"
- kv "github.com/strimertul/kilovolt/v5"
+ kv "github.com/strimertul/kilovolt/v6"
)
var (
- ErrNotAuthenticated = errors.New("not authenticated")
ErrSubscriptionNotFound = errors.New("subscription not found")
ErrEmptyKey = errors.New("key empty or unset")
)
@@ 41,8 43,9 @@ type Client struct {
}
type ClientOptions struct {
- Headers http.Header
- Logger logrus.FieldLogger
+ Headers http.Header
+ Password string
+ Logger logrus.FieldLogger
}
func NewClient(endpoint string, options ClientOptions) (*Client, error) {
@@ 62,14 65,60 @@ func NewClient(endpoint string, options ClientOptions) (*Client, error) {
}
err := client.ConnectToWebsocket()
+ if err != nil {
+ return nil, err
+ }
+
+ if options.Password != "" {
+ err = client.Authenticate(options.Password)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return client, nil
+}
+
+func (s *Client) Authenticate(password string) error {
+ res, err := s.makeRequest(kv.Request{
+ CmdName: kv.CmdAuthRequest,
+ })
+ if err != nil {
+ return err
+ }
+
+ data := res.Data.(map[string]interface{})
+
+ // Decode challenge
+ challengeBytes, err := base64.StdEncoding.DecodeString(data["challenge"].(string))
+ if err != nil {
+ return fmt.Errorf("failed to decode challenge: %w", err)
+ }
+ saltBytes, err := base64.StdEncoding.DecodeString(data["salt"].(string))
+ if err != nil {
+ return fmt.Errorf("failed to decode salt: %w", err)
+ }
- return client, err
+ // Create hash from password and challenge
+ hash := hmac.New(sha256.New, append([]byte(password), saltBytes...))
+ hash.Write(challengeBytes)
+ hashBytes := hash.Sum(nil)
+
+ // Send auth challenge
+ _, err = s.makeRequest(kv.Request{
+ CmdName: kv.CmdAuthChallenge,
+ Data: map[string]interface{}{
+ "hash": base64.StdEncoding.EncodeToString(hashBytes),
+ },
+ })
+ return err
}
-func (s *Client) Close() {
+func (s *Client) Close() error {
if s.ws != nil {
- s.ws.Close()
+ return s.ws.Close()
}
+ return nil
}
func (s *Client) ConnectToWebsocket() error {
@@ 136,9 185,9 @@ func (s *Client) ConnectToWebsocket() error {
}
}
// Deliver to prefix subscritpions
- for kv := range s.prefixsubs.IterBuffered() {
- if strings.HasPrefix(push.Key, kv.Key) {
- for _, chann := range kv.Val.([]chan KeyValuePair) {
+ for pair := range s.prefixsubs.IterBuffered() {
+ if strings.HasPrefix(push.Key, pair.Key) {
+ for _, chann := range pair.Val.([]chan KeyValuePair) {
chann <- KeyValuePair{push.Key, push.NewValue}
}
}
M client_test.go => client_test.go +31 -2
@@ 9,7 9,7 @@ import (
"github.com/dgraph-io/badger/v3"
"github.com/sirupsen/logrus"
- kv "github.com/strimertul/kilovolt/v5"
+ kv "github.com/strimertul/kilovolt/v6"
)
func TestCommands(t *testing.T) {
@@ 267,6 267,35 @@ func TestKeyList(t *testing.T) {
}
}
+func TestAuthentication(t *testing.T) {
+ log := logrus.New()
+ log.Level = logrus.TraceLevel
+
+ // Create hub with password
+ const password = "testPassword"
+ server, hub := createInMemoryKV(t, log)
+ hub.SetOptions(kv.HubOptions{
+ Password: password,
+ })
+
+ // Create client with password option
+ client, err := NewClient(server.URL, ClientOptions{
+ Logger: log,
+ Password: password,
+ })
+ if err != nil {
+ t.Fatal("error creating kv client", err.Error())
+ }
+
+ // Couple test operations to test if auth actually went correctly
+ if err = client.SetKey("test", "testvalue1234"); err != nil {
+ t.Fatal("error modifying key", err.Error())
+ }
+ if _, err = client.GetKey("test"); err != nil {
+ t.Fatal("error getting key")
+ }
+}
+
func createInMemoryKV(t *testing.T, log logrus.FieldLogger) (*httptest.Server, *kv.Hub) {
// Open in-memory DB
options := badger.DefaultOptions("").WithInMemory(true).WithLogger(log)
@@ 276,7 305,7 @@ func createInMemoryKV(t *testing.T, log logrus.FieldLogger) (*httptest.Server, *
}
// Create hub with in-mem DB
- hub, err := kv.NewHub(db, log)
+ hub, err := kv.NewHub(db, kv.HubOptions{}, log)
if err != nil {
t.Fatal("hub initialization failed", err.Error())
}
M cmd/kvcli/main.go => cmd/kvcli/main.go +1 -1
@@ 7,7 7,7 @@ import (
"os"
"strings"
- kvclient "github.com/strimertul/kilovolt-client-go/v3"
+ kvclient "github.com/strimertul/kilovolt-client-go/v6"
)
func check(err error) {
M go.mod => go.mod +2 -2
@@ 1,4 1,4 @@
-module github.com/strimertul/kilovolt-client-go/v3
+module github.com/strimertul/kilovolt-client-go/v6
go 1.16
@@ 8,5 8,5 @@ require (
github.com/json-iterator/go v1.1.11
github.com/orcaman/concurrent-map v0.0.0-20210501183033-44dafcb38ecc
github.com/sirupsen/logrus v1.8.1
- github.com/strimertul/kilovolt/v5 v5.0.1
+ github.com/strimertul/kilovolt/v6 v6.0.0
)
M go.sum => go.sum +2 -4
@@ 90,10 90,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/strimertul/kilovolt/v4 v4.0.1 h1:81isohdSixVURO2+dZKKZBPw97HJmNN4/BXn6ADFoWM=
-github.com/strimertul/kilovolt/v4 v4.0.1/go.mod h1:AO2ZFQtSB+AcjCw0RTkXjbM6XBAjhsXsrRq10BX95kw=
-github.com/strimertul/kilovolt/v5 v5.0.1 h1:LHAVqb3SrXiew3loTpYuPdz16Nl8/aTReBYj56xwF7I=
-github.com/strimertul/kilovolt/v5 v5.0.1/go.mod h1:HxfnnlEGhY6p+Im9U7pso07HEV+cXEsJH7uFTM7c6uE=
+github.com/strimertul/kilovolt/v6 v6.0.0 h1:0vkg3Vc0ploLuCkoF9v30vMPrmTryf26socXoklkYLo=
+github.com/strimertul/kilovolt/v6 v6.0.0/go.mod h1:O5Rwg8o66omRP4O3qInBKreW9jILZz2MEq4MuotzAXw=
github.com/twitchyliquid64/golang-asm v0.15.0/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=