Skip to content

Commit

Permalink
[ADD] cli: keygen command
Browse files Browse the repository at this point in the history
The command connects to a running instance. By default "localhost:8080".
The spec is: MASTERKEY CHANNEL ACCESS [ -t=<ttl>] [ -h=<host> ]

Note: If we start adding more commands, ideally the cli should move to a
separate project.

Closes: #430
  • Loading branch information
Florimond committed Oct 8, 2024
1 parent aa1afff commit 777d4a9
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 206 deletions.
13 changes: 8 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require (
github.com/emitter-io/config v1.0.0
github.com/emitter-io/stats v1.0.3
github.com/golang/snappy v0.0.4
github.com/gorilla/websocket v1.5.1
github.com/gorilla/websocket v1.5.3
github.com/jawher/mow.cli v1.2.0
github.com/kelindar/binary v1.0.18
github.com/kelindar/rate v1.0.0
Expand All @@ -25,11 +25,13 @@ require (
github.com/tidwall/pretty v1.2.1 // indirect
github.com/valyala/fasthttp v1.51.0
github.com/weaveworks/mesh v0.0.0-20191105120815-58dbcc3e8e63
golang.org/x/crypto v0.18.0
golang.org/x/net v0.20.0 // indirect
golang.org/x/crypto v0.25.0
golang.org/x/net v0.27.0 // indirect
gopkg.in/alexcesaro/statsd.v2 v2.0.0
)

require github.com/eclipse/paho.mqtt.golang v1.5.0

require (
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
Expand Down Expand Up @@ -60,8 +62,9 @@ require (
github.com/tidwall/tinyqueue v0.1.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fp
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o=
github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk=
github.com/emitter-io/address v1.0.1 h1:1bTyviCKDkB60rrFpHr6BY3AKjNWP3InY/WO6qPKkVU=
github.com/emitter-io/address v1.0.1/go.mod h1:kF9+NdGTAvzRKMd78zjj7MN01yDB9fPwZl2waJRlfYU=
github.com/emitter-io/config v1.0.0 h1:qpJMei4v3KL0Z5vsspVful/JIRr6v4zg8R28pG4Ry4M=
Expand Down Expand Up @@ -100,6 +102,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/gopherjs/gopherjs v0.0.0-20200209183636-89e6cbcd0b6d/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
Expand Down Expand Up @@ -224,6 +228,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
Expand All @@ -242,13 +248,17 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand All @@ -259,10 +269,14 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
Expand Down
248 changes: 47 additions & 201 deletions internal/command/keygen/keygen.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,229 +15,75 @@
package keygen

import (
"net"
"strings"
"time"
"encoding/json"
"fmt"

"github.com/emitter-io/emitter/internal/network/mqtt"
mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/emitter-io/emitter/internal/errors"
"github.com/emitter-io/emitter/internal/provider/logging"
"github.com/emitter-io/emitter/internal/security"
"github.com/emitter-io/emitter/internal/service/keygen"
cli "github.com/jawher/mow.cli"
)

// Generate a new channel key.
func NewKey(cmd *cli.Cmd) {
//cmd.Spec = "MASTERKEY CHANNEL ACCESS [ -ttl=<ttl>] [ -h=<host> ]"
//cmd.Spec = "MASTERKEY CHANNEL ACCESS [ -t=<ttl>] [ -h=<host> ]" // [ -t=<ttl>]"
cmd.Spec = "[ -h=<host> ]"
cmd.Spec = "MASTERKEY CHANNEL ACCESS [ -t=<ttl>] [ -h=<host> ]"
var (
/*
masterkey = cmd.StringArg("MASTERKEY", "", "Specifies the master key for generating channel keys.")
channel = cmd.StringArg("CHANNEL", "", "Specifies the name channel for which to generate key.")
access = cmd.StringArg("ACCESS", "", "Specifies the access rights for the channel (rwslpex).")
ttl = cmd.IntOpt("t ttl", 0, "Specifies the time to live for the key in seconds. By default, the key will never expire.")
*/
host = cmd.StringOpt("h host", "127.0.0.1:8080", "Specifies the broker host name and port. This must follow the <ip:port> format.")
masterkey = cmd.StringArg("MASTERKEY", "", "Specifies the master key for generating channel keys.")
channel = cmd.StringArg("CHANNEL", "", "Specifies the name channel for which to generate key.")
access = cmd.StringArg("ACCESS", "", "Specifies the access rights for the channel (rwslpex).")
ttl = cmd.IntOpt("t ttl", 0, "Specifies the time to live for the key in seconds. By default, the key will never expire.")
host = cmd.StringOpt("h host", "127.0.0.1:8080", "Specifies the broker host name and port. This must follow the <ip:port> format.")
)

cmd.Action = func() {

socket, err := net.Dial("tcp", *host)
if err != nil {
logging.LogError("client", "tcp connection to the broker", err)
return
// Create a channel to receive the response.
respChan := make(chan []byte)

// Create a new MQTT client.
opts := mqtt.NewClientOptions().AddBroker(fmt.Sprintf("tcp://%s", *host))
opts.SetDefaultPublishHandler(func(client mqtt.Client, m mqtt.Message) {
respChan <- m.Payload()
})
client := mqtt.NewClient(opts)

// Connect to the MQTT broker.
if token := client.Connect(); token.Wait() && token.Error() != nil {
panic(fmt.Errorf("failed to connect: %v", token.Error()))
}
defer socket.Close()

// Connect to the broker
logging.LogTarget("client", "connecting to the broker", *host)
connect := mqtt.Connect{}
if _, err := connect.EncodeTo(socket); err != nil {
logging.LogError("client", "mqtt connection to the broker", err)
return
// Publish the request.
request := keygen.Request{
Key: *masterkey,
Channel: *channel,
Type: *access,
TTL: int32(*ttl),
}

// Subscribe to the topic
logging.LogTarget("client", "subscribing to the channel", "emitter/keygen/")
/*
pkt, err := mqtt.DecodePacket(socket, 65536)
if err != nil {
return
}
*/
//sub := mqtt.
/*.Subscribe{
Header: mqtt.Header{QOS: 0},
Subscriptions: []mqtt.TopicQOSTuple{
{Topic: []byte("emitter/keygen/"), Qos: 0},
},
}
if _, err := sub.EncodeTo(socket); err != nil {
logging.LogError("client", "subscribe to the broker", err)
payload, err := json.Marshal(request)
if err != nil {
logging.LogError("keygen", "marshaling the request", err)
return
}*/

pub := mqtt.Publish{
Header: mqtt.Header{QOS: 0},
Topic: []byte("emitter/keygen/"),
Payload: []byte("Hello, World!"),
}
if _, err := pub.EncodeTo(socket); err != nil {
logging.LogError("client", "publish to the broker", err)
if token := client.Publish("emitter/keygen/", 1, false, payload); token.Wait() && token.Error() != nil {
logging.LogError("keygen", "publishing the request", token.Error())
return
}

/*
fmt.Println("Masterkey: ", *masterkey)
// TODO do not hardcode config file
cfg := config.New("emitter.conf", dynamo.NewProvider(), vault.NewProvider(config.VaultUser))
var err error
var lic license.License
// Parse the license
if lic, err = license.Parse(cfg.License); err != nil {
logging.LogError("keygen", "parsing the license", err)
return
}
// Create a new cipher from the licence provided
var cipher license.Cipher
cipher, err = lic.Cipher()
if err != nil {
logging.LogError("keygen", "creating a cipher", err)
return
}
metering := config.LoadProvider(cfg.Metering, usage.NewNoop(), usage.NewHTTP()).(usage.Metering)
logging.LogTarget("service", "configured usage metering", metering.Name())
// Load the contract provider
contracts := config.LoadProvider(cfg.Contract,
contract.NewSingleContractProvider(lic, metering),
contract.NewHTTPContractProvider(lic, metering)).(contract.Provider)
logging.LogTarget("service", "configured contracts provider", contracts.Name())
service := keygen.New(cipher, contracts, nil) // The authorizer is not used in this case.
var key string
fmt.Println("Masterkey: ", *masterkey)
fmt.Println("Channel: ", channel)
fmt.Println("Access: ", access)
/*
if key, err = service.CreateKey(*masterkey, *channel, accessToInt(*access), expires(*ttl)); err != nil {
logging.LogError("keygen", "generating the key", err)
return
}*/

//key, _ = service.CreateKey(*masterkey, *channel, accessToInt(*access), expires(*ttl))
//logging.LogAction("keygen", fmt.Sprintf("generated new key: %v", key))
}
}

func expires(ttl int) time.Time {
if ttl == 0 {
return time.Unix(0, 0)
}

return time.Now().Add(time.Duration(ttl) * time.Second).UTC()
}

func accessToInt(s string) uint8 {
required := security.AllowNone
if strings.Contains(s, "r") {
required |= security.AllowRead
}
if strings.Contains(s, "w") {
required |= security.AllowWrite
}
if strings.Contains(s, "l") {
required |= security.AllowLoad
}
if strings.Contains(s, "s") {
required |= security.AllowStore
}
if strings.Contains(s, "p") {
required |= security.AllowPresence
}
if strings.Contains(s, "e") {
required |= security.AllowExtend
}
if strings.Contains(s, "x") {
required |= security.AllowExecute
}

return required
}

/*
cmd.Action = func() {
cli, err := newConn(*host, *key, *channel)
if err != nil {
logging.LogError("client", "connection to the broker", err)
// Wait for the response and check whether it's an error.
response := <-respChan
var errResponse errors.Error
if err := json.Unmarshal(response, &errResponse); err == nil && errResponse.Error() != "" {
logging.LogError("keygen", "generating the key", fmt.Errorf("error: %s", errResponse.Error()))
return
}

defer cli.Close()
go cli.Drain()
logging.LogAction("client", "draining messages...")
msg := newMessage(cli.topic, *size)
for {
for i := 0; i < *batch; i++ {
atomic.AddUint64(&cli.sent, 1)
if _, err := msg.EncodeTo(cli); err != nil {
logging.LogError("client", "tcp send", err)
return
}
}
time.Sleep(1 * time.Millisecond)
// Parse the response and print the key.
keygen := keygen.Response{}
if err := json.Unmarshal(response, &keygen); err != nil {
logging.LogError("keygen", "parsing the response", err)
return
}
fmt.Println(keygen.Key)
client.Disconnect(250)
}
}
// NewConn creates a new connection for the load test.
func newConn(hostAndPort, key, channel string) (*conn, error) {
socket, err := dial("tcp", hostAndPort)
if err != nil {
return nil, err
}
cli := &conn{
Conn: socket,
scratch: make([]byte, 1),
topic: fmt.Sprintf("%s/%s", key, channel),
}
// Connect to the broker
logging.LogTarget("client", "connecting to the broker", hostAndPort)
connect := mqtt.Connect{ClientID: []byte("load-tester")}
if _, err := connect.EncodeTo(cli); err != nil {
return nil, err
}
cli.Skip(mqtt.TypeOfConnack)
// Subscribe to the topic
sub := mqtt.Subscribe{
Header: mqtt.Header{QOS: 0},
Subscriptions: []mqtt.TopicQOSTuple{
{Topic: []byte(cli.topic), Qos: 0},
},
}
if _, err := sub.EncodeTo(cli); err != nil {
return nil, err
}
logging.LogTarget("client", "subscribing to the channel", channel)
return cli, nil
}
// Conn represents a connection to use for the load test.
type conn struct {
net.Conn
scratch []byte
topic string
sent uint64
}
*/

0 comments on commit 777d4a9

Please sign in to comment.