1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-04-24 13:48:49 +08:00

MF-415 - Merge mProxy support (#1045)

* NOISSUE - Add mProxy support (#1017)

* Add mproxy

Signed-off-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>

* Fix docker and add EMQ compose

Signed-off-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>

* Fix EMQX name

Signed-off-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>

* Add nats, auth and es

Signed-off-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>

* Removed unucessary vendoring

Signed-off-by: Drasko Draskovic <drasko.draskovic@gmail.com>

* Update vendoring

Signed-off-by: Drasko Draskovic <drasko.draskovic@gmail.com>

* Fix mproxy interface implementation

Signed-off-by: Drasko Draskovic <drasko.draskovic@gmail.com>

 NOISSUE - Aligned Event interface method signatures with new spec (#1025)

* Aligned Event interface method signatures with new spec

Signed-off-by: Nikola Marcetic <n.marcetic86@gmail.com>

* Updated deps

Signed-off-by: Nikola Marcetic <n.marcetic86@gmail.com>

NOISSUE - Update mproxy dependency (#1038)

Signed-off-by: Nikola Marcetic <n.marcetic86@gmail.com>

Update Vendor with new mProxy (#1043)

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

Twins merge conflict reverted

Signed-off-by: Nikola Marcetic <n.marcetic86@gmail.com>

Twins merge conflict reverted

Signed-off-by: Nikola Marcetic <n.marcetic86@gmail.com>

Twins fixed nats import

Signed-off-by: Nikola Marcetic <n.marcetic86@gmail.com>

Update deps

Signed-off-by: Nikola Marcetic <n.marcetic86@gmail.com>

* Resolved GolangCI remarks

Signed-off-by: Nikola Marcetic <n.marcetic86@gmail.com>

Resolved GolangCI remarks

Signed-off-by: Nikola Marcetic <n.marcetic86@gmail.com>

Resolved GolangCI remarks

Signed-off-by: Nikola Marcetic <n.marcetic86@gmail.com>

* Fixed Event interface Unsubscribe() typo

Signed-off-by: Nikola Marcetic <n.marcetic86@gmail.com>

* Update vendors

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

* Upgrade CI script

Signed-off-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>

Co-authored-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>
Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
This commit is contained in:
Nikola Marčetić 2020-02-26 17:14:16 +01:00 committed by GitHub
parent 3b5d51276f
commit 42b3682352
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
530 changed files with 83755 additions and 42629 deletions

View File

@ -4,7 +4,7 @@
BUILD_DIR = build
SERVICES = users things http ws coap lora influxdb-writer influxdb-reader mongodb-writer \
mongodb-reader cassandra-writer cassandra-reader postgres-writer postgres-reader cli \
bootstrap opcua authn twins
bootstrap opcua authn twins mproxy
DOCKERS = $(addprefix docker_,$(SERVICES))
DOCKERS_DEV = $(addprefix docker_dev_,$(SERVICES))
CGO_ENABLED ?= 0

View File

@ -14,7 +14,7 @@ import (
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux/authn/postgres"
dockertest "gopkg.in/ory-am/dockertest.v3"
dockertest "gopkg.in/ory/dockertest.v3"
)
const wrong string = "wrong-value"

View File

@ -12,7 +12,7 @@ import (
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux/bootstrap/postgres"
"github.com/mainflux/mainflux/logger"
dockertest "gopkg.in/ory-am/dockertest.v3"
dockertest "gopkg.in/ory/dockertest.v3"
)
const (

View File

@ -10,7 +10,7 @@ import (
"testing"
"github.com/go-redis/redis"
dockertest "gopkg.in/ory-am/dockertest.v3"
dockertest "gopkg.in/ory/dockertest.v3"
)
const (

View File

@ -23,7 +23,7 @@ import (
"github.com/mainflux/mainflux/writers"
"github.com/mainflux/mainflux/writers/api"
"github.com/mainflux/mainflux/writers/cassandra"
nats "github.com/nats-io/go-nats"
nats "github.com/nats-io/nats.go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
)

View File

@ -29,7 +29,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
broker "github.com/nats-io/go-nats"
broker "github.com/nats-io/nats.go"
)
const (

View File

@ -24,7 +24,7 @@ import (
"github.com/mainflux/mainflux/http/nats"
"github.com/mainflux/mainflux/logger"
thingsapi "github.com/mainflux/mainflux/things/api/auth/grpc"
broker "github.com/nats-io/go-nats"
broker "github.com/nats-io/nats.go"
opentracing "github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"

View File

@ -21,7 +21,7 @@ import (
"github.com/mainflux/mainflux/writers"
"github.com/mainflux/mainflux/writers/api"
"github.com/mainflux/mainflux/writers/influxdb"
nats "github.com/nats-io/go-nats"
nats "github.com/nats-io/nats.go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
)

View File

@ -23,7 +23,7 @@ import (
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/mainflux/mainflux/lora/redis"
nats "github.com/nats-io/go-nats"
nats "github.com/nats-io/nats.go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
)

View File

@ -21,7 +21,7 @@ import (
"github.com/mainflux/mainflux/writers"
"github.com/mainflux/mainflux/writers/api"
"github.com/mainflux/mainflux/writers/mongodb"
nats "github.com/nats-io/go-nats"
nats "github.com/nats-io/nats.go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"

227
cmd/mproxy/main.go Normal file
View File

@ -0,0 +1,227 @@
package main
import (
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/signal"
"strconv"
"syscall"
"time"
"github.com/go-redis/redis"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/logger"
mqtt "github.com/mainflux/mainflux/mqtt/mproxy"
"github.com/mainflux/mainflux/mqtt/mproxy/nats"
mr "github.com/mainflux/mainflux/mqtt/mproxy/redis"
thingsapi "github.com/mainflux/mainflux/things/api/auth/grpc"
"github.com/mainflux/mproxy/pkg/events"
mp "github.com/mainflux/mproxy/pkg/mqtt"
broker "github.com/nats-io/nats.go"
opentracing "github.com/opentracing/opentracing-go"
jconfig "github.com/uber/jaeger-client-go/config"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
defMQTTHost = "0.0.0.0"
defMQTTPort = "1883"
defMQTTTargetHost = "0.0.0.0"
defMQTTTargetPort = "1884"
envMQTTHost = "MF_MQTT_ADAPTER_MQTT_HOST"
envMQTTPort = "MF_MQTT_ADAPTER_MQTT_PORT"
envMQTTTargetHost = "MF_MQTT_ADAPTER_MQTT_TARGET_HOST"
envMQTTTargetPort = "MF_MQTT_ADAPTER_MQTT_TARGET_PORT"
defLogLevel = "error"
envLogLevel = "MF_MQTT_ADAPTER_LOG_LEVEL"
defThingsURL = "localhost:8181"
defThingsTimeout = "1" // in seconds
envThingsURL = "MF_THINGS_URL"
envThingsTimeout = "MF_MQTT_ADAPTER_THINGS_TIMEOUT"
defNatsURL = broker.DefaultURL
envNatsURL = "MF_NATS_URL"
defJaegerURL = ""
envJaegerURL = "MF_JAEGER_URL"
defClientTLS = "false"
defCACerts = ""
envClientTLS = "MF_MQTT_ADAPTER_CLIENT_TLS"
envCACerts = "MF_MQTT_ADAPTER_CA_CERTS"
envInstance = "MF_MQTT_ADAPTER_INSTANCE"
defInstance = ""
envESURL = "MF_MQTT_ADAPTER_ES_URL"
envESPass = "MF_MQTT_ADAPTER_ES_PASS"
envESDB = "MF_MQTT_ADAPTER_ES_DB"
defESURL = "localhost:6379"
defESPass = ""
defESDB = "0"
)
type config struct {
mqttHost string
mqttPort string
mqttTargetHost string
mqttTargetPort string
jaegerURL string
logLevel string
thingsURL string
thingsTimeout time.Duration
natsURL string
clientTLS bool
caCerts string
instance string
esURL string
esPass string
esDB string
}
func main() {
cfg := loadConfig()
logger, err := logger.New(os.Stdout, cfg.logLevel)
if err != nil {
log.Fatalf(err.Error())
}
nc, err := broker.Connect(cfg.natsURL)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to NATS: %s", err))
os.Exit(1)
}
defer nc.Close()
conn := connectToThings(cfg, logger)
defer conn.Close()
tracer, closer := initJaeger("mproxy", cfg.jaegerURL, logger)
defer closer.Close()
thingsTracer, thingsCloser := initJaeger("things", cfg.jaegerURL, logger)
defer thingsCloser.Close()
cc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsTimeout)
pub := nats.NewMessagePublisher(nc)
rc := connectToRedis(cfg.esURL, cfg.esPass, cfg.esDB, logger)
defer rc.Close()
es := mr.NewEventStore(rc, cfg.instance)
// Event handler for MQTT hooks
evt := mqtt.New(cc, pub, es, logger, tracer)
errs := make(chan error, 2)
logger.Info(fmt.Sprintf("Starting MQTT proxy on port %s ", cfg.mqttPort))
go proxyMQTT(cfg, logger, evt, errs)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT)
errs <- fmt.Errorf("%s", <-c)
}()
err = <-errs
logger.Error(fmt.Sprintf("mProxy terminated: %s", err))
}
func loadConfig() config {
tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envClientTLS)
}
timeout, err := strconv.ParseInt(mainflux.Env(envThingsTimeout, defThingsTimeout), 10, 64)
if err != nil {
log.Fatalf("Invalid %s value: %s", envThingsTimeout, err.Error())
}
return config{
mqttHost: mainflux.Env(envMQTTHost, defMQTTHost),
mqttPort: mainflux.Env(envMQTTPort, defMQTTPort),
mqttTargetHost: mainflux.Env(envMQTTTargetHost, defMQTTTargetHost),
mqttTargetPort: mainflux.Env(envMQTTTargetPort, defMQTTTargetPort),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
thingsTimeout: time.Duration(timeout) * time.Second,
thingsURL: mainflux.Env(envThingsURL, defThingsURL),
natsURL: mainflux.Env(envNatsURL, defNatsURL),
logLevel: mainflux.Env(envLogLevel, defLogLevel),
clientTLS: tls,
caCerts: mainflux.Env(envCACerts, defCACerts),
instance: mainflux.Env(envInstance, defInstance),
esURL: mainflux.Env(envESURL, defESURL),
esPass: mainflux.Env(envESPass, defESPass),
esDB: mainflux.Env(envESDB, defESDB),
}
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger client: %s", err))
os.Exit(1)
}
return tracer, closer
}
func connectToThings(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to load certs: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
logger.Info("gRPC communication is not encrypted")
opts = append(opts, grpc.WithInsecure())
}
conn, err := grpc.Dial(cfg.thingsURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err))
os.Exit(1)
}
return conn
}
func connectToRedis(redisURL, redisPass, redisDB string, logger logger.Logger) *redis.Client {
db, err := strconv.Atoi(redisDB)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to redis: %s", err))
os.Exit(1)
}
return redis.NewClient(&redis.Options{
Addr: redisURL,
Password: redisPass,
DB: db,
})
}
func proxyMQTT(cfg config, logger logger.Logger, evt events.Event, errs chan error) {
mp := mp.New(cfg.mqttHost, cfg.mqttPort, cfg.mqttTargetHost, cfg.mqttTargetPort, evt, logger)
errs <- mp.Proxy()
}

View File

@ -24,7 +24,7 @@ import (
"github.com/mainflux/mainflux/opcua/redis"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
nats "github.com/nats-io/go-nats"
nats "github.com/nats-io/nats.go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
)

View File

@ -21,7 +21,7 @@ import (
"github.com/mainflux/mainflux/writers"
"github.com/mainflux/mainflux/writers/api"
"github.com/mainflux/mainflux/writers/postgres"
nats "github.com/nats-io/go-nats"
nats "github.com/nats-io/nats.go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
)

View File

@ -27,7 +27,7 @@ import (
natspub "github.com/mainflux/mainflux/twins/nats/publisher"
natssub "github.com/mainflux/mainflux/twins/nats/subscriber"
"github.com/mainflux/mainflux/twins/uuid"
nats "github.com/nats-io/go-nats"
nats "github.com/nats-io/nats.go"
opentracing "github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"

View File

@ -22,7 +22,7 @@ import (
adapter "github.com/mainflux/mainflux/ws"
"github.com/mainflux/mainflux/ws/api"
"github.com/mainflux/mainflux/ws/nats"
broker "github.com/nats-io/go-nats"
broker "github.com/nats-io/nats.go"
opentracing "github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"

View File

@ -13,7 +13,7 @@ import (
"time"
"github.com/mainflux/mainflux"
broker "github.com/nats-io/go-nats"
broker "github.com/nats-io/nats.go"
)
const (

View File

@ -11,7 +11,7 @@ import (
"github.com/gogo/protobuf/proto"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/coap"
broker "github.com/nats-io/go-nats"
broker "github.com/nats-io/nats.go"
)
const prefix = "channel"

47
docker/mproxy.yml Normal file
View File

@ -0,0 +1,47 @@
# Copyright (c) Mainflux
# SPDX-License-Identifier: Apache-2.0
version: "3.7"
volumes:
mainflux-mqtt-redis-volume:
services:
nginx:
depends_on:
- mqtt-adapter
emqx:
image: emqx/emqx:latest
container_name: mainflux-emqx
restart: on-failure
environment:
EMQX_LISTENER__TCP__EXTERNAL: 1884
ports:
- 1884:1884
networks:
- mainflux-base-net
mqtt-adapter:
image: mainflux/mproxy:latest
container_name: mainflux-mqtt
depends_on:
- emqx
- things
- nats
restart: on-failure
environment:
MF_MQTT_ADAPTER_LOG_LEVEL: ${MF_MQTT_ADAPTER_LOG_LEVEL}
MF_MQTT_ADAPTER_PORT: ${MF_MQTT_ADAPTER_PORT}
MF_MQTT_ADAPTER_WS_PORT: ${MF_MQTT_ADAPTER_WS_PORT}
MF_MQTT_ADAPTER_ES_HOST: es-redis
MF_NATS_URL: ${MF_NATS_URL}
MF_THINGS_URL: things:${MF_THINGS_AUTH_GRPC_PORT}
MF_JAEGER_URL: ${MF_JAEGER_URL}
MF_MQTT_ADAPTER_MQTT_TARGET_HOST: emqx
MF_MQTT_ADAPTER_MQTT_TARGET_PORT: 1884
ports:
- 18831:${MF_MQTT_ADAPTER_PORT}
- 8881:${MF_MQTT_ADAPTER_WS_PORT}
networks:
- mainflux-base-net

55
go.mod
View File

@ -3,68 +3,51 @@ module github.com/mainflux/mainflux
go 1.11
require (
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/BurntSushi/toml v0.3.1
github.com/Microsoft/go-winio v0.4.7 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/VividCortex/gohistogram v1.0.0 // indirect
github.com/cenkalti/backoff v2.0.0+incompatible // indirect
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect
github.com/containerd/continuity v0.0.0-20180416230128-c6cef3483023 // indirect
github.com/bitly/go-hostpool v0.1.0 // indirect
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/docker/docker v1.13.1
github.com/docker/go-connections v0.3.0 // indirect
github.com/docker/go-units v0.3.3 // indirect
github.com/dustin/go-coap v0.0.0-20170214053734-ddcc80675fa4
github.com/eclipse/paho.mqtt.golang v1.2.0
github.com/fatih/color v1.7.0
github.com/go-kit/kit v0.9.0
github.com/go-redis/redis v6.15.0+incompatible
github.com/go-zoo/bone v1.3.0
github.com/gobuffalo/packr v1.30.1 // indirect
github.com/gocql/gocql v0.0.0-20181106112037-68ae1e384be4
github.com/gofrs/uuid v3.2.0+incompatible
github.com/gogo/protobuf v1.3.1
github.com/golang/protobuf v1.3.2
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
github.com/golang/protobuf v1.3.3
github.com/gopcua/opcua v0.1.6
github.com/gorilla/websocket v1.4.1
github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect
github.com/hokaccha/go-prettyjson v0.0.0-20180920040306-f579f869bbfe
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/influxdata/influxdb v1.6.4
github.com/jmoiron/sqlx v1.2.1-0.20190319043955-cdf62fdf55f6
github.com/lib/pq v1.0.0
github.com/mainflux/mproxy v0.1.0
github.com/mainflux/senml v1.0.0
github.com/mattn/go-colorable v0.0.9 // indirect
github.com/mattn/go-isatty v0.0.3 // indirect
github.com/nats-io/gnatsd v1.4.1 // indirect
github.com/nats-io/go-nats v1.6.0
github.com/nats-io/nuid v1.0.0 // indirect
github.com/onsi/ginkgo v1.10.3 // indirect
github.com/onsi/gomega v1.7.1 // indirect
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runc v0.1.1 // indirect
github.com/nats-io/nats-server/v2 v2.1.4 // indirect
github.com/nats-io/nats.go v1.9.1
github.com/opentracing/opentracing-go v1.1.0
github.com/ory/dockertest v3.3.0+incompatible // indirect
github.com/pelletier/go-toml v1.4.0 // indirect
github.com/prometheus/client_golang v0.9.3
github.com/rubenv/sql-migrate v0.0.0-20181106121204-ba2c6a7295c5
github.com/sony/gobreaker v0.0.0-20180905101324-b2a34562d02c
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cobra v0.0.3
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/cobra v0.0.5
github.com/spf13/viper v1.5.0
github.com/stretchr/testify v1.4.0
github.com/tidwall/pretty v1.0.0 // indirect
github.com/uber/jaeger-client-go v2.16.0+incompatible
github.com/uber/jaeger-lib v2.0.0+incompatible // indirect
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect
github.com/xdg/stringprep v1.0.1-0.20180714160509-73f8eece6fdc // indirect
github.com/uber/jaeger-client-go v2.22.1+incompatible
github.com/ziutek/mymysql v1.5.4 // indirect
go.mongodb.org/mongo-driver v1.1.3
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5
golang.org/x/net v0.0.0-20190522155817-f3200d17e092
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
golang.org/x/text v0.3.2 // indirect
gonum.org/v1/gonum v0.0.0-20190808205415-ced62fe5104b
google.golang.org/grpc v1.24.0
gopkg.in/gorp.v1 v1.7.1 // indirect
gopkg.in/ory-am/dockertest.v3 v3.3.2
google.golang.org/genproto v0.0.0-20200225123651-fc8f55426688 // indirect
google.golang.org/grpc v1.27.1
gopkg.in/ory/dockertest.v3 v3.3.5
gotest.tools v2.2.0+incompatible // indirect
)

137
go.sum
View File

@ -17,11 +17,14 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bitly/go-hostpool v0.1.0 h1:XKmsF6k5el6xHG3WPJ8U0Ku/ye7njX7W81Ng7O2ioR0=
github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/cenkalti/backoff v2.0.0+incompatible h1:5IIPUHhlnUZbcHQsQou5k1Tn58nJkeJL9U+ig5CHJbY=
github.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cisco/senml v0.0.0-20181031221301-910a55054e16 h1:gVMpF6unIWOG8JgCt3XhlYdT3lRFDcMMVJdMesU+TQY=
github.com/cisco/senml v0.0.0-20181031221301-910a55054e16/go.mod h1:pLFASTQEf1nGfvoMwxmOjLeImwMKQMx18w38UcI9ZfI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
@ -29,9 +32,11 @@ github.com/containerd/continuity v0.0.0-20180416230128-c6cef3483023 h1:ydDbSX89i
github.com/containerd/continuity v0.0.0-20180416230128-c6cef3483023/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -48,6 +53,8 @@ github.com/dustin/go-coap v0.0.0-20170214053734-ddcc80675fa4 h1:+3t0PiNl/W3Cl4/+
github.com/dustin/go-coap v0.0.0-20170214053734-ddcc80675fa4/go.mod h1:as2rZ2aojRzZF8bGx1bPAn1yi9ICG6LwkiPOj6PBtjc=
github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t2y2qayIX0=
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
@ -56,7 +63,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fxamacker/cbor v1.3.2 h1:jMCvPyzpTVWoe1jRDUFPupVoV+DzDvnc1VP+9VU4ql8=
github.com/fxamacker/cbor v1.3.2/go.mod h1:Uy2lR31/2WfmW0yiA4i3t+we5kF3B/wzKsttcux+i/g=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.7.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
@ -68,13 +74,18 @@ github.com/go-redis/redis v6.15.0+incompatible h1:/Wib9cA7CF3SQxBZRMHyQvqzlwzc8P
github.com/go-redis/redis v6.15.0+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.7.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-zoo/bone v0.0.0-20160911183509-fd0aebc74e90 h1:r19yr6hoJJ+0ANoSBJn98J3ONZFfB4CwvrtYxRdQVeo=
github.com/go-zoo/bone v0.0.0-20160911183509-fd0aebc74e90/go.mod h1:oqsroXM1ZcoSPNsxaSy2JNMJMSC/A463LSB0Vnoa2SI=
github.com/go-zoo/bone v1.3.0 h1:PY6sHq37FnQhj+4ZyqFIzJQHvrrGx0GEc3vTZZC/OsI=
github.com/go-zoo/bone v1.3.0/go.mod h1:HI3Lhb7G3UQcAwEhOJ2WyNcsFtQX1WYHa0Hl4OBbhW8=
github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
github.com/gocql/gocql v0.0.0-20181106112037-68ae1e384be4 h1:n5NlV76GU6337XT+jarynqONI5LlqaYkTPaFZ25og6g=
github.com/gocql/gocql v0.0.0-20181106112037-68ae1e384be4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
@ -92,17 +103,23 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopcua/opcua v0.1.6 h1:B9SVRKQGzcWcwP2QPYN93Uku32+3wL+v5cgzBxE6V5I=
github.com/gopcua/opcua v0.1.6/go.mod h1:INwnDoRxmNWAt7+tzqxuGqQkSF2c1C69VAL0c2q6AcY=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
@ -120,29 +137,34 @@ github.com/influxdata/influxdb v1.6.4 h1:K8wPlkrP02HzHTJbbUQQ1CZ2Hw6LtpG4xbNEgnl
github.com/influxdata/influxdb v1.6.4/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
github.com/jmoiron/sqlx v1.2.1-0.20190319043955-cdf62fdf55f6 h1:6KHlj0TRbiafVOoCXCixS0GbdWF/paU373ABxEzhY6s=
github.com/jmoiron/sqlx v1.2.1-0.20190319043955-cdf62fdf55f6/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mainflux/senml v0.0.0-20191128185934-061491bfd7d6 h1:l5qGnZOGbiVnuclQYib6QiiQ2wA0k5vRdVdiJrTUsjw=
github.com/mainflux/senml v0.0.0-20191128185934-061491bfd7d6/go.mod h1:g9i8pj4WMs29KkUpXivbe/PP0qJd1kt3b1CF77S8A3s=
github.com/mainflux/senml v0.0.0-20191129155045-8e0c95e00589 h1:xoR2XyJGqBlReRXzABImJobL6l1CkgdUfJxQANvAq6A=
github.com/mainflux/senml v0.0.0-20191129155045-8e0c95e00589/go.mod h1:g9i8pj4WMs29KkUpXivbe/PP0qJd1kt3b1CF77S8A3s=
github.com/mainflux/mainflux v0.0.0-20191223163044-f42f2095bab4/go.mod h1:K3ghSIpAqwv5F/t30LO57+11S7tE97ur2Z6wWEHa2CA=
github.com/mainflux/mproxy v0.1.0 h1:h6zLgLW25+hURhvmlX5HIQIRKu4p/YJkptcsgcAhwis=
github.com/mainflux/mproxy v0.1.0/go.mod h1:HrT+8h62rGLkFDmaOoPg5CLgEURlvPfT3jr/q5y/Qw0=
github.com/mainflux/senml v1.0.0 h1:oLS5aBhvdHjgQ8kfq3jX7yD+DaquhvpyvIWNsPil3X0=
github.com/mainflux/senml v1.0.0/go.mod h1:g9i8pj4WMs29KkUpXivbe/PP0qJd1kt3b1CF77S8A3s=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
@ -151,18 +173,30 @@ github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.0/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/gnatsd v1.4.1 h1:RconcfDeWpKCD6QIIwiVFcvForlXpWeJP7i5/lDLy44=
github.com/nats-io/gnatsd v1.4.1/go.mod h1:nqco77VO78hLCJpIcVfygDP2rPGfsEHkGTUk94uh5DQ=
github.com/nats-io/go-nats v1.6.0 h1:FznPwMfrVwGnSCh7JTXyJDRW0TIkD4Tr+M1LPJt9T70=
github.com/nats-io/go-nats v1.6.0/go.mod h1:+t7RHT5ApZebkrQdnn6AhQJmhJJiKAvJUio1PiiCtj0=
github.com/nats-io/jwt v0.3.0 h1:xdnzwFETV++jNc4W1mw//qFyJGb2ABOombmZJQS4+Qo=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.4 h1:BILRnsJ2Yb/fefiFbBWADpViGF69uh4sxe8poVDQ06g=
github.com/nats-io/nats-server/v2 v2.1.4/go.mod h1:Jw1Z28soD/QasIA2uWjXyM9El1jly3YwyFOuR8tH1rg=
github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0 h1:qMd4+pRHgdr1nAClu+2h/2a5F2TmKcCzjCDazVgRoX4=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.0 h1:44QGdhbiANq8ZCbUkdn6W5bqtg+mHuDE4wOUuxxndFs=
github.com/nats-io/nuid v1.0.0/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
@ -179,6 +213,7 @@ github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsq
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/ory/dockertest v3.3.0+incompatible h1:r+Us+ELHPI8CudFL+l/wr7CrG6phWQ8jaqX0Sgx+OF0=
github.com/ory/dockertest v3.3.0+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
@ -191,25 +226,29 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20180426121432-d811d2e9bf89/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20180408092902-8b1c2da0d56d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rubenv/sql-migrate v0.0.0-20181106121204-ba2c6a7295c5 h1:4fmVndFuOuFREP0MPVT+bymEYx/mvRBnmGamM1YfH8c=
github.com/rubenv/sql-migrate v0.0.0-20181106121204-ba2c6a7295c5/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY=
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sony/gobreaker v0.0.0-20180905101324-b2a34562d02c h1:7EMc5KMRVlkzEyK5n4YqdPEsmO+6AlAGCJiqnqW6n2Y=
github.com/sony/gobreaker v0.0.0-20180905101324-b2a34562d02c/go.mod h1:XvpJiTD8NibaH7z0NzyfhR1+NQDtR9F/x92xheTwC9k=
@ -221,20 +260,21 @@ github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4=
github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
@ -244,89 +284,122 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/uber/jaeger-client-go v2.16.0+incompatible h1:Q2Pp6v3QYiocMxomCaJuwQGFt7E53bPYqEgug/AoBtY=
github.com/uber/jaeger-client-go v2.16.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-client-go v2.22.1+incompatible h1:NHcubEkVbahf9t3p75TOCR83gdUHXjRJvjoBh1yACsM=
github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v2.0.0+incompatible h1:iMSCV0rmXEogjNWPh2D0xk9YVKvrtGoHJNe9ebLu/pw=
github.com/uber/jaeger-lib v2.0.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ugorji/go v1.1.1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/x448/senml v0.2.0 h1:W6P7p4bA8ZvsEkl6+f7kjoc9n5vPHBYL5V6kGJkyDy4=
github.com/x448/senml v0.2.0/go.mod h1:dAsCrOGmYEKO/L/DUpZpinKEgI1PCxAUjt2eAwXCDx8=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
github.com/xdg/stringprep v1.0.1-0.20180714160509-73f8eece6fdc h1:vIp1tjhVogU0yBy7w96P027ewvNPeH6gzuNcoc+NReU=
github.com/xdg/stringprep v1.0.1-0.20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.mongodb.org/mongo-driver v1.0.0 h1:KxPRDyfB2xXnDE2My8acoOWBQkfv3tz0SaWTRZjJR0c=
go.mongodb.org/mongo-driver v1.0.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.3 h1:++7u8r9adKhGR+I79NfEtYrk2ktjenErXM99PSufIoI=
go.mongodb.org/mongo-driver v1.1.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2 h1:y102fOLFqhV41b+4GPiJoa0k/x+pJcEi2/HB1Y5T6fU=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
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=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180509002218-f73e4c9ed3b7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180510032850-7dfd1290c791/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed h1:uPxWBzB3+mlnjy9W58qY1j/cjyFjutgw/Vhan2zLy/A=
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e h1:D5TXcfTk7xF7hvieo4QErS3qqCB4teTffacDWr7CI+0=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.0.0-20190808205415-ced62fe5104b h1:wlZ2AJblZitrh7dfm5OX2WenXLBZCuWqUeNczop2lPA=
gonum.org/v1/gonum v0.0.0-20190808205415-ced62fe5104b/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180427144745-86e600f69ee4/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200225123651-fc8f55426688 h1:1+0Z5cgv1eDXJD9z2tdQF9PSSQnJXwism490hJydMRI=
google.golang.org/genproto v0.0.0-20200225123651-fc8f55426688/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gorp.v1 v1.7.1 h1:GBB9KrWRATQZh95HJyVGUZrWwOPswitEYEyqlK8JbAA=
@ -335,6 +408,8 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ory-am/dockertest.v3 v3.3.2 h1:NgIHJacfXajJResc7luKYPF/F2kul6MXqbleEjv4PAY=
gopkg.in/ory-am/dockertest.v3 v3.3.2/go.mod h1:s9mmoLkaGeAh97qygnNj4xWkiN7e1SKekYC6CovU+ek=
gopkg.in/ory/dockertest.v3 v3.3.5 h1:bm2RXztqdTSinb1tUP9/iFTPmhy3sk2EL2k9GSMKNEE=
gopkg.in/ory/dockertest.v3 v3.3.5/go.mod h1:wI78nwA6jQZVXv3va0CcbJAuftRnAa063zO5Fek7+uI=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@ -344,6 +419,8 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@ -10,7 +10,7 @@ import (
"github.com/gogo/protobuf/proto"
"github.com/mainflux/mainflux"
broker "github.com/nats-io/go-nats"
broker "github.com/nats-io/nats.go"
)
const prefix = "channel"

View File

@ -10,7 +10,7 @@ import (
"github.com/gogo/protobuf/proto"
"github.com/mainflux/mainflux"
broker "github.com/nats-io/go-nats"
broker "github.com/nats-io/nats.go"
)
var _ mainflux.MessagePublisher = (*natsPublisher)(nil)

206
mqtt/mproxy/mproxy.go Normal file
View File

@ -0,0 +1,206 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
package mproxy
import (
"context"
"errors"
"fmt"
"net/url"
"regexp"
"strings"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/mqtt/mproxy/redis"
"github.com/mainflux/mproxy/pkg/events"
opentracing "github.com/opentracing/opentracing-go"
)
var (
_ events.Event = (*Event)(nil)
channelRegExp = regexp.MustCompile(`^\/?channels\/([\w\-]+)\/messages(\/[^?]*)?(\?.*)?$`)
ctRegExp = regexp.MustCompile(`^(\/.*)?\/ct\/([^\/]+)$`)
errMalformedTopic = errors.New("malformed topic")
errMalformedData = errors.New("malformed request data")
errMalformedSubtopic = errors.New("malformed subtopic")
)
// Event implements events.Event interface
type Event struct {
tc mainflux.ThingsServiceClient
mp mainflux.MessagePublisher
tracer opentracing.Tracer
logger logger.Logger
es redis.EventStore
}
// New creates new Event entity
func New(tc mainflux.ThingsServiceClient, mp mainflux.MessagePublisher, es redis.EventStore,
logger logger.Logger, tracer opentracing.Tracer) *Event {
return &Event{
tc: tc,
mp: mp,
es: es,
tracer: tracer,
logger: logger,
}
}
// AuthRegister is called on device connection,
// prior forwarding to the MQTT broker
func (e *Event) AuthRegister(username, clientID *string, password *[]byte) error {
e.logger.Info(fmt.Sprintf("AuthRegister() - clientID: %s, username: %s",
*clientID, *username))
t := &mainflux.Token{
Value: string(*password),
}
thid, err := e.tc.Identify(context.TODO(), t)
if err != nil {
return err
}
if thid.Value != *username {
return err
}
return nil
}
func (e *Event) authAccess(username string, topic string) error {
// Topics are in the format:
// channels/<channel_id>/messages/<subtopic>/.../ct/<content_type>
if !channelRegExp.Match([]byte(topic)) {
e.logger.Info(fmt.Sprintf("Malformed topic %s", topic))
return errMalformedTopic
}
channelParts := channelRegExp.FindStringSubmatch(topic)
if len(channelParts) < 1 {
return errMalformedData
}
chanID := channelParts[1]
ar := &mainflux.AccessByIDReq{
ThingID: username,
ChanID: chanID,
}
_, err := e.tc.CanAccessByID(context.TODO(), ar)
if err != nil {
return err
}
return nil
}
// AuthPublish is called on device publish,
// prior forwarding to the MQTT broker
func (e *Event) AuthPublish(username, clientID string, topic *string, payload *[]byte) error {
e.logger.Info(fmt.Sprintf("AuthPublish() - clientID: %s, topic: %s", clientID, *topic))
return e.authAccess(username, *topic)
}
// AuthSubscribe is called on device publish,
// prior forwarding to the MQTT broker
func (e *Event) AuthSubscribe(username, clientID string, topics *[]string) error {
e.logger.Info(fmt.Sprintf("AuthSubscribe() - clientID: %s, topics: %s", clientID, strings.Join(*topics, ",")))
for _, v := range *topics {
if err := e.authAccess(username, v); err != nil {
return err
}
}
return nil
}
// Register - after client sucesfully connected
func (e *Event) Register(clientID string) {
e.logger.Info(fmt.Sprintf("Register() - clientID: %s", clientID))
}
func parseSubtopic(subtopic string) (string, error) {
if subtopic == "" {
return subtopic, nil
}
subtopic, err := url.QueryUnescape(subtopic)
if err != nil {
return "", errMalformedSubtopic
}
subtopic = strings.Replace(subtopic, "/", ".", -1)
elems := strings.Split(subtopic, ".")
filteredElems := []string{}
for _, elem := range elems {
if elem == "" {
continue
}
if len(elem) > 1 && (strings.Contains(elem, "*") || strings.Contains(elem, ">")) {
return "", errMalformedSubtopic
}
filteredElems = append(filteredElems, elem)
}
subtopic = strings.Join(filteredElems, ".")
return subtopic, nil
}
// Publish - after client sucesfully published
func (e *Event) Publish(clientID, topic string, payload []byte) {
e.logger.Info(fmt.Sprintf("Publish() - clientID: %s, topic: %s", clientID, topic))
// Topics are in the format:
// channels/<channel_id>/messages/<subtopic>/.../ct/<content_type>
channelParts := channelRegExp.FindStringSubmatch(topic)
if len(channelParts) < 1 {
e.logger.Info(fmt.Sprintf("Error in mqtt publish %s", errMalformedData))
return
}
chanID := channelParts[1]
subtopic := channelParts[2]
ct := ""
if stParts := ctRegExp.FindStringSubmatch(subtopic); len(stParts) > 1 {
ct = stParts[2]
subtopic = stParts[1]
}
subtopic, err := parseSubtopic(subtopic)
if err != nil {
e.logger.Info(fmt.Sprintf("Error in mqtt publish %s", err))
return
}
msg := mainflux.Message{
Protocol: "mqtt",
ContentType: ct,
Channel: chanID,
Subtopic: subtopic,
Payload: payload,
}
if err := e.mp.Publish(context.TODO(), "", msg); err != nil {
e.logger.Info(fmt.Sprintf("Error in mqtt publish %s", err))
return
}
}
// Subscribe - after client sucesfully subscribed
func (e *Event) Subscribe(clientID string, topics []string) {
e.logger.Info(fmt.Sprintf("Subscribe() - clientID: %s, topics: %s", clientID, strings.Join(topics, ",")))
}
// Unubscribe - after client unsubscribed
func (e *Event) Unsubscribe(clientID string, topics []string) {
e.logger.Info(fmt.Sprintf("Unubscribe() - clientID: %s, topics: %s", clientID, strings.Join(topics, ",")))
}

5
mqtt/mproxy/nats/doc.go Normal file
View File

@ -0,0 +1,5 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
// Package nats contains NATS message publisher implementation.
package nats

View File

@ -0,0 +1,39 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
package nats
import (
"context"
"fmt"
"github.com/gogo/protobuf/proto"
"github.com/mainflux/mainflux"
broker "github.com/nats-io/nats.go"
)
const prefix = "channel"
var _ mainflux.MessagePublisher = (*natsPublisher)(nil)
type natsPublisher struct {
nc *broker.Conn
}
// NewMessagePublisher instantiates NATS message publisher.
func NewMessagePublisher(nc *broker.Conn) mainflux.MessagePublisher {
return &natsPublisher{nc: nc}
}
func (pub *natsPublisher) Publish(_ context.Context, _ string, msg mainflux.Message) error {
data, err := proto.Marshal(&msg)
if err != nil {
return err
}
subject := fmt.Sprintf("%s.%s", prefix, msg.Channel)
if msg.Subtopic != "" {
subject = fmt.Sprintf("%s.%s", subject, msg.Subtopic)
}
return pub.nc.Publish(subject, data)
}

6
mqtt/mproxy/redis/doc.go Normal file
View File

@ -0,0 +1,6 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
// Package redis contains cache implementations using Redis as
// the underlying database.
package redis

View File

@ -0,0 +1,28 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
package redis
type event interface {
Encode() map[string]interface{}
}
var (
_ event = (*mqttEvent)(nil)
)
type mqttEvent struct {
clientID string
timestamp string
eventType string
instance string
}
func (me mqttEvent) Encode() map[string]interface{} {
return map[string]interface{}{
"thing_id": me.clientID,
"timestamp": me.timestamp,
"event_type": me.eventType,
"instance": me.instance,
}
}

View File

@ -0,0 +1,65 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
package redis
import (
"strconv"
"time"
"github.com/go-redis/redis"
)
const (
streamID = "mainflux.mqtt"
streamLen = 1000
)
// EventStore is a struct used to store event streams in Redis
type EventStore struct {
client *redis.Client
instance string
}
// NewEventStore returns wrapper around mProxy service that sends
// events to event store.
func NewEventStore(client *redis.Client, instance string) EventStore {
return EventStore{
client: client,
instance: instance,
}
}
func (es EventStore) storeEvent(clientID, eventType string) error {
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
event := mqttEvent{
clientID: clientID,
timestamp: timestamp,
eventType: eventType,
instance: es.instance,
}
record := &redis.XAddArgs{
Stream: streamID,
MaxLenApprox: streamLen,
Values: event.Encode(),
}
if err := es.client.XAdd(record).Err(); err != nil {
return err
}
return nil
}
// Connect issues event on MQTT CONNECT
func (es EventStore) Connect(clientID string) error {
return es.storeEvent(clientID, "connect")
}
// Disconnect issues event on MQTT CONNECT
func (es EventStore) Disconnect(clientID string) error {
return es.storeEvent(clientID, "disconnect")
}

View File

@ -9,7 +9,7 @@ import (
"github.com/gogo/protobuf/proto"
"github.com/mainflux/mainflux"
broker "github.com/nats-io/go-nats"
broker "github.com/nats-io/nats.go"
)
var _ mainflux.MessagePublisher = (*natsPublisher)(nil)

View File

@ -11,7 +11,7 @@ import (
"github.com/gocql/gocql"
log "github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/writers/cassandra"
dockertest "gopkg.in/ory-am/dockertest.v3"
dockertest "gopkg.in/ory/dockertest.v3"
)
var logger, _ = log.New(os.Stdout, log.Info.String())

View File

@ -7,7 +7,7 @@ import (
"time"
influxdb "github.com/influxdata/influxdb/client/v2"
dockertest "gopkg.in/ory-am/dockertest.v3"
dockertest "gopkg.in/ory/dockertest.v3"
)
func TestMain(m *testing.M) {

View File

@ -12,7 +12,7 @@ import (
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
dockertest "gopkg.in/ory-am/dockertest.v3"
dockertest "gopkg.in/ory/dockertest.v3"
)
func TestMain(m *testing.M) {

View File

@ -14,7 +14,7 @@ import (
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/readers/postgres"
dockertest "gopkg.in/ory-am/dockertest.v3"
dockertest "gopkg.in/ory/dockertest.v3"
)
const (

View File

@ -1,10 +1,10 @@
# This script contains commands to be executed by the CI tool.
NPROC=$(nproc)
GO_VERSION=1.13
PROTOC_VERSION=3.11.1
PROTOC_GEN_VERSION=v1.3.2
PROTOC_VERSION=3.11.4
PROTOC_GEN_VERSION=v1.3.3
PROTOC_GOFAST_VERSION=v1.3.1
GRPC_VERSION=v1.24.0
GRPC_VERSION=v1.27.1
function version_gt() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; }

View File

@ -14,7 +14,7 @@ import (
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/things/postgres"
dockertest "gopkg.in/ory-am/dockertest.v3"
dockertest "gopkg.in/ory/dockertest.v3"
)
const (

View File

@ -10,7 +10,7 @@ import (
"testing"
"github.com/go-redis/redis"
dockertest "gopkg.in/ory-am/dockertest.v3"
dockertest "gopkg.in/ory/dockertest.v3"
)
const (

View File

@ -12,7 +12,7 @@ import (
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
dockertest "gopkg.in/ory-am/dockertest.v3"
dockertest "gopkg.in/ory/dockertest.v3"
)
const (

View File

@ -10,7 +10,7 @@ import (
"github.com/gogo/protobuf/proto"
"github.com/mainflux/mainflux"
"github.com/nats-io/go-nats"
"github.com/nats-io/nats.go"
)
const (

View File

@ -11,7 +11,7 @@ import (
"github.com/mainflux/mainflux"
log "github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/twins"
"github.com/nats-io/go-nats"
nats "github.com/nats-io/nats.go"
)
const (

View File

@ -14,7 +14,7 @@ import (
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux/users/postgres"
dockertest "gopkg.in/ory-am/dockertest.v3"
dockertest "gopkg.in/ory/dockertest.v3"
)
const wrong string = "wrong-value"

View File

@ -393,7 +393,7 @@ func (p *Buffer) Bytes() []byte { return p.buf }
// than relying on this API.
//
// If deterministic serialization is requested, map entries will be sorted
// by keys in lexographical order. This is an implementation detail and
// by keys in lexicographical order. This is an implementation detail and
// subject to change.
func (p *Buffer) SetDeterministic(deterministic bool) {
p.deterministic = deterministic

View File

@ -456,6 +456,8 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
return nil
}
var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
// writeAny writes an arbitrary field.
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
v = reflect.Indirect(v)
@ -519,8 +521,8 @@ func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Propert
// mutating this value.
v = v.Addr()
}
if etm, ok := v.Interface().(encoding.TextMarshaler); ok {
text, err := etm.MarshalText()
if v.Type().Implements(textMarshalerType) {
text, err := v.Interface().(encoding.TextMarshaler).MarshalText()
if err != nil {
return err
}

9
vendor/github.com/google/uuid/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,9 @@
language: go
go:
- 1.4.3
- 1.5.3
- tip
script:
- go test -v ./...

10
vendor/github.com/google/uuid/CONTRIBUTING.md generated vendored Normal file
View File

@ -0,0 +1,10 @@
# How to contribute
We definitely welcome patches and contribution to this project!
### Legal requirements
In order to protect both you and ourselves, you will need to sign the
[Contributor License Agreement](https://cla.developers.google.com/clas).
You may have already signed it for other Google projects.

9
vendor/github.com/google/uuid/CONTRIBUTORS generated vendored Normal file
View File

@ -0,0 +1,9 @@
Paul Borman <borman@google.com>
bmatsuo
shawnps
theory
jboverfelt
dsymonds
cd1
wallclockbuilder
dansouza

27
vendor/github.com/google/uuid/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2009,2014 Google Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

19
vendor/github.com/google/uuid/README.md generated vendored Normal file
View File

@ -0,0 +1,19 @@
# uuid ![build status](https://travis-ci.org/google/uuid.svg?branch=master)
The uuid package generates and inspects UUIDs based on
[RFC 4122](http://tools.ietf.org/html/rfc4122)
and DCE 1.1: Authentication and Security Services.
This package is based on the github.com/pborman/uuid package (previously named
code.google.com/p/go-uuid). It differs from these earlier packages in that
a UUID is a 16 byte array rather than a byte slice. One loss due to this
change is the ability to represent an invalid UUID (vs a NIL UUID).
###### Install
`go get github.com/google/uuid`
###### Documentation
[![GoDoc](https://godoc.org/github.com/google/uuid?status.svg)](http://godoc.org/github.com/google/uuid)
Full `go doc` style documentation for the package can be viewed online without
installing this package by using the GoDoc site here:
http://godoc.org/github.com/google/uuid

80
vendor/github.com/google/uuid/dce.go generated vendored Normal file
View File

@ -0,0 +1,80 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"encoding/binary"
"fmt"
"os"
)
// A Domain represents a Version 2 domain
type Domain byte
// Domain constants for DCE Security (Version 2) UUIDs.
const (
Person = Domain(0)
Group = Domain(1)
Org = Domain(2)
)
// NewDCESecurity returns a DCE Security (Version 2) UUID.
//
// The domain should be one of Person, Group or Org.
// On a POSIX system the id should be the users UID for the Person
// domain and the users GID for the Group. The meaning of id for
// the domain Org or on non-POSIX systems is site defined.
//
// For a given domain/id pair the same token may be returned for up to
// 7 minutes and 10 seconds.
func NewDCESecurity(domain Domain, id uint32) (UUID, error) {
uuid, err := NewUUID()
if err == nil {
uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
uuid[9] = byte(domain)
binary.BigEndian.PutUint32(uuid[0:], id)
}
return uuid, err
}
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
// domain with the id returned by os.Getuid.
//
// NewDCESecurity(Person, uint32(os.Getuid()))
func NewDCEPerson() (UUID, error) {
return NewDCESecurity(Person, uint32(os.Getuid()))
}
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
// domain with the id returned by os.Getgid.
//
// NewDCESecurity(Group, uint32(os.Getgid()))
func NewDCEGroup() (UUID, error) {
return NewDCESecurity(Group, uint32(os.Getgid()))
}
// Domain returns the domain for a Version 2 UUID. Domains are only defined
// for Version 2 UUIDs.
func (uuid UUID) Domain() Domain {
return Domain(uuid[9])
}
// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2
// UUIDs.
func (uuid UUID) ID() uint32 {
return binary.BigEndian.Uint32(uuid[0:4])
}
func (d Domain) String() string {
switch d {
case Person:
return "Person"
case Group:
return "Group"
case Org:
return "Org"
}
return fmt.Sprintf("Domain%d", int(d))
}

12
vendor/github.com/google/uuid/doc.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package uuid generates and inspects UUIDs.
//
// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security
// Services.
//
// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to
// maps or compared directly.
package uuid

1
vendor/github.com/google/uuid/go.mod generated vendored Normal file
View File

@ -0,0 +1 @@
module github.com/google/uuid

53
vendor/github.com/google/uuid/hash.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"crypto/md5"
"crypto/sha1"
"hash"
)
// Well known namespace IDs and UUIDs
var (
NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
Nil UUID // empty UUID, all zeros
)
// NewHash returns a new UUID derived from the hash of space concatenated with
// data generated by h. The hash should be at least 16 byte in length. The
// first 16 bytes of the hash are used to form the UUID. The version of the
// UUID will be the lower 4 bits of version. NewHash is used to implement
// NewMD5 and NewSHA1.
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
h.Reset()
h.Write(space[:])
h.Write(data)
s := h.Sum(nil)
var uuid UUID
copy(uuid[:], s)
uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
return uuid
}
// NewMD5 returns a new MD5 (Version 3) UUID based on the
// supplied name space and data. It is the same as calling:
//
// NewHash(md5.New(), space, data, 3)
func NewMD5(space UUID, data []byte) UUID {
return NewHash(md5.New(), space, data, 3)
}
// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
// supplied name space and data. It is the same as calling:
//
// NewHash(sha1.New(), space, data, 5)
func NewSHA1(space UUID, data []byte) UUID {
return NewHash(sha1.New(), space, data, 5)
}

37
vendor/github.com/google/uuid/marshal.go generated vendored Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import "fmt"
// MarshalText implements encoding.TextMarshaler.
func (uuid UUID) MarshalText() ([]byte, error) {
var js [36]byte
encodeHex(js[:], uuid)
return js[:], nil
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (uuid *UUID) UnmarshalText(data []byte) error {
id, err := ParseBytes(data)
if err == nil {
*uuid = id
}
return err
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (uuid UUID) MarshalBinary() ([]byte, error) {
return uuid[:], nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (uuid *UUID) UnmarshalBinary(data []byte) error {
if len(data) != 16 {
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
}
copy(uuid[:], data)
return nil
}

90
vendor/github.com/google/uuid/node.go generated vendored Normal file
View File

@ -0,0 +1,90 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"sync"
)
var (
nodeMu sync.Mutex
ifname string // name of interface being used
nodeID [6]byte // hardware for version 1 UUIDs
zeroID [6]byte // nodeID with only 0's
)
// NodeInterface returns the name of the interface from which the NodeID was
// derived. The interface "user" is returned if the NodeID was set by
// SetNodeID.
func NodeInterface() string {
defer nodeMu.Unlock()
nodeMu.Lock()
return ifname
}
// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
// If name is "" then the first usable interface found will be used or a random
// Node ID will be generated. If a named interface cannot be found then false
// is returned.
//
// SetNodeInterface never fails when name is "".
func SetNodeInterface(name string) bool {
defer nodeMu.Unlock()
nodeMu.Lock()
return setNodeInterface(name)
}
func setNodeInterface(name string) bool {
iname, addr := getHardwareInterface(name) // null implementation for js
if iname != "" && addr != nil {
ifname = iname
copy(nodeID[:], addr)
return true
}
// We found no interfaces with a valid hardware address. If name
// does not specify a specific interface generate a random Node ID
// (section 4.1.6)
if name == "" {
ifname = "random"
randomBits(nodeID[:])
return true
}
return false
}
// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
// if not already set.
func NodeID() []byte {
defer nodeMu.Unlock()
nodeMu.Lock()
if nodeID == zeroID {
setNodeInterface("")
}
nid := nodeID
return nid[:]
}
// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
// of id are used. If id is less than 6 bytes then false is returned and the
// Node ID is not set.
func SetNodeID(id []byte) bool {
if len(id) < 6 {
return false
}
defer nodeMu.Unlock()
nodeMu.Lock()
copy(nodeID[:], id)
ifname = "user"
return true
}
// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
func (uuid UUID) NodeID() []byte {
var node [6]byte
copy(node[:], uuid[10:])
return node[:]
}

12
vendor/github.com/google/uuid/node_js.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build js
package uuid
// getHardwareInterface returns nil values for the JS version of the code.
// This remvoves the "net" dependency, because it is not used in the browser.
// Using the "net" library inflates the size of the transpiled JS code by 673k bytes.
func getHardwareInterface(name string) (string, []byte) { return "", nil }

33
vendor/github.com/google/uuid/node_net.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !js
package uuid
import "net"
var interfaces []net.Interface // cached list of interfaces
// getHardwareInterface returns the name and hardware address of interface name.
// If name is "" then the name and hardware address of one of the system's
// interfaces is returned. If no interfaces are found (name does not exist or
// there are no interfaces) then "", nil is returned.
//
// Only addresses of at least 6 bytes are returned.
func getHardwareInterface(name string) (string, []byte) {
if interfaces == nil {
var err error
interfaces, err = net.Interfaces()
if err != nil {
return "", nil
}
}
for _, ifs := range interfaces {
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
return ifs.Name, ifs.HardwareAddr
}
}
return "", nil
}

59
vendor/github.com/google/uuid/sql.go generated vendored Normal file
View File

@ -0,0 +1,59 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"database/sql/driver"
"fmt"
)
// Scan implements sql.Scanner so UUIDs can be read from databases transparently
// Currently, database types that map to string and []byte are supported. Please
// consult database-specific driver documentation for matching types.
func (uuid *UUID) Scan(src interface{}) error {
switch src := src.(type) {
case nil:
return nil
case string:
// if an empty UUID comes from a table, we return a null UUID
if src == "" {
return nil
}
// see Parse for required string format
u, err := Parse(src)
if err != nil {
return fmt.Errorf("Scan: %v", err)
}
*uuid = u
case []byte:
// if an empty UUID comes from a table, we return a null UUID
if len(src) == 0 {
return nil
}
// assumes a simple slice of bytes if 16 bytes
// otherwise attempts to parse
if len(src) != 16 {
return uuid.Scan(string(src))
}
copy((*uuid)[:], src)
default:
return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
}
return nil
}
// Value implements sql.Valuer so that UUIDs can be written to databases
// transparently. Currently, UUIDs map to strings. Please consult
// database-specific driver documentation for matching types.
func (uuid UUID) Value() (driver.Value, error) {
return uuid.String(), nil
}

123
vendor/github.com/google/uuid/time.go generated vendored Normal file
View File

@ -0,0 +1,123 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"encoding/binary"
"sync"
"time"
)
// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
// 1582.
type Time int64
const (
lillian = 2299160 // Julian day of 15 Oct 1582
unix = 2440587 // Julian day of 1 Jan 1970
epoch = unix - lillian // Days between epochs
g1582 = epoch * 86400 // seconds between epochs
g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
)
var (
timeMu sync.Mutex
lasttime uint64 // last time we returned
clockSeq uint16 // clock sequence for this run
timeNow = time.Now // for testing
)
// UnixTime converts t the number of seconds and nanoseconds using the Unix
// epoch of 1 Jan 1970.
func (t Time) UnixTime() (sec, nsec int64) {
sec = int64(t - g1582ns100)
nsec = (sec % 10000000) * 100
sec /= 10000000
return sec, nsec
}
// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
// clock sequence as well as adjusting the clock sequence as needed. An error
// is returned if the current time cannot be determined.
func GetTime() (Time, uint16, error) {
defer timeMu.Unlock()
timeMu.Lock()
return getTime()
}
func getTime() (Time, uint16, error) {
t := timeNow()
// If we don't have a clock sequence already, set one.
if clockSeq == 0 {
setClockSequence(-1)
}
now := uint64(t.UnixNano()/100) + g1582ns100
// If time has gone backwards with this clock sequence then we
// increment the clock sequence
if now <= lasttime {
clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000
}
lasttime = now
return Time(now), clockSeq, nil
}
// ClockSequence returns the current clock sequence, generating one if not
// already set. The clock sequence is only used for Version 1 UUIDs.
//
// The uuid package does not use global static storage for the clock sequence or
// the last time a UUID was generated. Unless SetClockSequence is used, a new
// random clock sequence is generated the first time a clock sequence is
// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1)
func ClockSequence() int {
defer timeMu.Unlock()
timeMu.Lock()
return clockSequence()
}
func clockSequence() int {
if clockSeq == 0 {
setClockSequence(-1)
}
return int(clockSeq & 0x3fff)
}
// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to
// -1 causes a new sequence to be generated.
func SetClockSequence(seq int) {
defer timeMu.Unlock()
timeMu.Lock()
setClockSequence(seq)
}
func setClockSequence(seq int) {
if seq == -1 {
var b [2]byte
randomBits(b[:]) // clock sequence
seq = int(b[0])<<8 | int(b[1])
}
oldSeq := clockSeq
clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant
if oldSeq != clockSeq {
lasttime = 0
}
}
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
// uuid. The time is only defined for version 1 and 2 UUIDs.
func (uuid UUID) Time() Time {
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
return Time(time)
}
// ClockSequence returns the clock sequence encoded in uuid.
// The clock sequence is only well defined for version 1 and 2 UUIDs.
func (uuid UUID) ClockSequence() int {
return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff
}

43
vendor/github.com/google/uuid/util.go generated vendored Normal file
View File

@ -0,0 +1,43 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"io"
)
// randomBits completely fills slice b with random data.
func randomBits(b []byte) {
if _, err := io.ReadFull(rander, b); err != nil {
panic(err.Error()) // rand should never fail
}
}
// xvalues returns the value of a byte as a hexadecimal digit or 255.
var xvalues = [256]byte{
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
}
// xtob converts hex characters x1 and x2 into a byte.
func xtob(x1, x2 byte) (byte, bool) {
b1 := xvalues[x1]
b2 := xvalues[x2]
return (b1 << 4) | b2, b1 != 255 && b2 != 255
}

245
vendor/github.com/google/uuid/uuid.go generated vendored Normal file
View File

@ -0,0 +1,245 @@
// Copyright 2018 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"bytes"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"io"
"strings"
)
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
// 4122.
type UUID [16]byte
// A Version represents a UUID's version.
type Version byte
// A Variant represents a UUID's variant.
type Variant byte
// Constants returned by Variant.
const (
Invalid = Variant(iota) // Invalid UUID
RFC4122 // The variant specified in RFC4122
Reserved // Reserved, NCS backward compatibility.
Microsoft // Reserved, Microsoft Corporation backward compatibility.
Future // Reserved for future definition.
)
var rander = rand.Reader // random function
// Parse decodes s into a UUID or returns an error. Both the standard UUID
// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the
// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex
// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
func Parse(s string) (UUID, error) {
var uuid UUID
switch len(s) {
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
case 36:
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
case 36 + 9:
if strings.ToLower(s[:9]) != "urn:uuid:" {
return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
}
s = s[9:]
// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
case 36 + 2:
s = s[1:]
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
case 32:
var ok bool
for i := range uuid {
uuid[i], ok = xtob(s[i*2], s[i*2+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
}
return uuid, nil
default:
return uuid, fmt.Errorf("invalid UUID length: %d", len(s))
}
// s is now at least 36 bytes long
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return uuid, errors.New("invalid UUID format")
}
for i, x := range [16]int{
0, 2, 4, 6,
9, 11,
14, 16,
19, 21,
24, 26, 28, 30, 32, 34} {
v, ok := xtob(s[x], s[x+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
uuid[i] = v
}
return uuid, nil
}
// ParseBytes is like Parse, except it parses a byte slice instead of a string.
func ParseBytes(b []byte) (UUID, error) {
var uuid UUID
switch len(b) {
case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) {
return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
}
b = b[9:]
case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
b = b[1:]
case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
var ok bool
for i := 0; i < 32; i += 2 {
uuid[i/2], ok = xtob(b[i], b[i+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
}
return uuid, nil
default:
return uuid, fmt.Errorf("invalid UUID length: %d", len(b))
}
// s is now at least 36 bytes long
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' {
return uuid, errors.New("invalid UUID format")
}
for i, x := range [16]int{
0, 2, 4, 6,
9, 11,
14, 16,
19, 21,
24, 26, 28, 30, 32, 34} {
v, ok := xtob(b[x], b[x+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
uuid[i] = v
}
return uuid, nil
}
// MustParse is like Parse but panics if the string cannot be parsed.
// It simplifies safe initialization of global variables holding compiled UUIDs.
func MustParse(s string) UUID {
uuid, err := Parse(s)
if err != nil {
panic(`uuid: Parse(` + s + `): ` + err.Error())
}
return uuid
}
// FromBytes creates a new UUID from a byte slice. Returns an error if the slice
// does not have a length of 16. The bytes are copied from the slice.
func FromBytes(b []byte) (uuid UUID, err error) {
err = uuid.UnmarshalBinary(b)
return uuid, err
}
// Must returns uuid if err is nil and panics otherwise.
func Must(uuid UUID, err error) UUID {
if err != nil {
panic(err)
}
return uuid
}
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
// , or "" if uuid is invalid.
func (uuid UUID) String() string {
var buf [36]byte
encodeHex(buf[:], uuid)
return string(buf[:])
}
// URN returns the RFC 2141 URN form of uuid,
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
func (uuid UUID) URN() string {
var buf [36 + 9]byte
copy(buf[:], "urn:uuid:")
encodeHex(buf[9:], uuid)
return string(buf[:])
}
func encodeHex(dst []byte, uuid UUID) {
hex.Encode(dst, uuid[:4])
dst[8] = '-'
hex.Encode(dst[9:13], uuid[4:6])
dst[13] = '-'
hex.Encode(dst[14:18], uuid[6:8])
dst[18] = '-'
hex.Encode(dst[19:23], uuid[8:10])
dst[23] = '-'
hex.Encode(dst[24:], uuid[10:])
}
// Variant returns the variant encoded in uuid.
func (uuid UUID) Variant() Variant {
switch {
case (uuid[8] & 0xc0) == 0x80:
return RFC4122
case (uuid[8] & 0xe0) == 0xc0:
return Microsoft
case (uuid[8] & 0xe0) == 0xe0:
return Future
default:
return Reserved
}
}
// Version returns the version of uuid.
func (uuid UUID) Version() Version {
return Version(uuid[6] >> 4)
}
func (v Version) String() string {
if v > 15 {
return fmt.Sprintf("BAD_VERSION_%d", v)
}
return fmt.Sprintf("VERSION_%d", v)
}
func (v Variant) String() string {
switch v {
case RFC4122:
return "RFC4122"
case Reserved:
return "Reserved"
case Microsoft:
return "Microsoft"
case Future:
return "Future"
case Invalid:
return "Invalid"
}
return fmt.Sprintf("BadVariant%d", int(v))
}
// SetRand sets the random number generator to r, which implements io.Reader.
// If r.Read returns an error when the package requests random data then
// a panic will be issued.
//
// Calling SetRand with nil sets the random number generator to the default
// generator.
func SetRand(r io.Reader) {
if r == nil {
rander = rand.Reader
return
}
rander = r
}

44
vendor/github.com/google/uuid/version1.go generated vendored Normal file
View File

@ -0,0 +1,44 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"encoding/binary"
)
// NewUUID returns a Version 1 UUID based on the current NodeID and clock
// sequence, and the current time. If the NodeID has not been set by SetNodeID
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
// be set NewUUID returns nil. If clock sequence has not been set by
// SetClockSequence then it will be set automatically. If GetTime fails to
// return the current NewUUID returns nil and an error.
//
// In most cases, New should be used.
func NewUUID() (UUID, error) {
nodeMu.Lock()
if nodeID == zeroID {
setNodeInterface("")
}
nodeMu.Unlock()
var uuid UUID
now, seq, err := GetTime()
if err != nil {
return uuid, err
}
timeLow := uint32(now & 0xffffffff)
timeMid := uint16((now >> 32) & 0xffff)
timeHi := uint16((now >> 48) & 0x0fff)
timeHi |= 0x1000 // Version 1
binary.BigEndian.PutUint32(uuid[0:], timeLow)
binary.BigEndian.PutUint16(uuid[4:], timeMid)
binary.BigEndian.PutUint16(uuid[6:], timeHi)
binary.BigEndian.PutUint16(uuid[8:], seq)
copy(uuid[10:], nodeID[:])
return uuid, nil
}

38
vendor/github.com/google/uuid/version4.go generated vendored Normal file
View File

@ -0,0 +1,38 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import "io"
// New creates a new random UUID or panics. New is equivalent to
// the expression
//
// uuid.Must(uuid.NewRandom())
func New() UUID {
return Must(NewRandom())
}
// NewRandom returns a Random (Version 4) UUID.
//
// The strength of the UUIDs is based on the strength of the crypto/rand
// package.
//
// A note about uniqueness derived from the UUID Wikipedia entry:
//
// Randomly generated UUIDs have 122 random bits. One's annual risk of being
// hit by a meteorite is estimated to be one chance in 17 billion, that
// means the probability is about 0.00000000006 (6 × 1011),
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
// year and having one duplicate.
func NewRandom() (UUID, error) {
var uuid UUID
_, err := io.ReadFull(rander, uuid[:])
if err != nil {
return Nil, err
}
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
return uuid, nil
}

View File

@ -26,6 +26,7 @@ The tool is sponsored by the [marvin + konsorten GmbH](http://www.konsorten.de).
We thank all the authors who provided code to this library:
* Felix Kollmann
* Nicolas Perraut
## License

View File

@ -0,0 +1,11 @@
// +build linux darwin
package sequences
import (
"fmt"
)
func EnableVirtualTerminalProcessing(stream uintptr, enable bool) error {
return fmt.Errorf("windows only package")
}

191
vendor/github.com/mainflux/mproxy/LICENSE generated vendored Normal file
View File

@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2015 Mainflux
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

28
vendor/github.com/mainflux/mproxy/pkg/events/events.go generated vendored Normal file
View File

@ -0,0 +1,28 @@
package events
// Event is an interface for mProxy hooks
type Event interface {
// Authorization on client `CONNECT`
// Each of the params are passed by reference, so that it can be changed
AuthRegister(username, clientID *string, password *[]byte) error
// Authorization on client `PUBLISH`
// Topic is passed by reference, so that it can be modified
AuthPublish(username, clientID string, topic *string, payload *[]byte) error
// Authorization on client `SUBSCRIBE`
// Topics are passed by reference, so that they can be modified
AuthSubscribe(username, clientID string, topics *[]string) error
// After client successfully connected
Register(clientID string)
// After client successfully published
Publish(clientID, topic string, payload []byte)
// After client successfully subscribed
Subscribe(clientID string, topics []string)
// After client unsubscribed
Unsubscribe(clientID string, topics []string)
}

82
vendor/github.com/mainflux/mproxy/pkg/mqtt/proxy.go generated vendored Normal file
View File

@ -0,0 +1,82 @@
package mqtt
import (
"fmt"
"io"
"net"
"github.com/google/uuid"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mproxy/pkg/events"
)
// Proxy is main MQTT proxy struct
type Proxy struct {
host string
port string
target string
event events.Event
logger logger.Logger
}
// New will setup a new Proxy struct after parsing the options
func New(host, port, targetHost, targetPort string, event events.Event, logger logger.Logger) *Proxy {
return &Proxy{
host: host,
port: port,
target: fmt.Sprintf("%s:%s", targetHost, targetPort),
event: event,
logger: logger,
}
}
func (p *Proxy) accept(l net.Listener) {
for {
conn, err := l.Accept()
if err != nil {
p.logger.Warn("Accept error " + err.Error())
continue
}
p.logger.Info("Accepted new client")
go p.handleConnection(conn)
}
}
func (p *Proxy) handleConnection(inbound net.Conn) {
defer inbound.Close()
outbound, err := net.Dial("tcp", p.target)
if err != nil {
p.logger.Error("Cannot connect to remote broker " + p.target)
return
}
defer outbound.Close()
uuid, err := uuid.NewRandom()
if err != nil {
return
}
s := newSession(uuid.String(), inbound, outbound, p.event, p.logger)
if err := s.stream(); err != io.EOF {
p.logger.Warn("Exited session " + s.id + "with error: " + err.Error())
}
s.logger.Info("Session " + s.id + "closed: " + s.outbound.LocalAddr().String())
}
// Proxy of the server, this will block.
func (p *Proxy) Proxy() error {
addr := fmt.Sprintf("%s:%s", p.host, p.port)
l, err := net.Listen("tcp", addr)
if err != nil {
return err
}
defer l.Close()
// Acceptor loop
p.accept(l)
p.logger.Info("Server Exiting...")
return nil
}

114
vendor/github.com/mainflux/mproxy/pkg/mqtt/session.go generated vendored Normal file
View File

@ -0,0 +1,114 @@
package mqtt
import (
"net"
"github.com/eclipse/paho.mqtt.golang/packets"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mproxy/pkg/events"
)
const (
up direction = iota
down
)
type direction int
type mqttClient struct {
ID string
username string
password []byte
}
type session struct {
id string
logger logger.Logger
inbound net.Conn
outbound net.Conn
event events.Event
client mqttClient
}
func newSession(uuid string, inbound, outbound net.Conn, event events.Event, logger logger.Logger) *session {
return &session{
id: uuid,
logger: logger,
inbound: inbound,
outbound: outbound,
event: event,
}
}
func (s *session) stream() error {
// In parallel read from client, send to broker
// and read from broker, send to client
errs := make(chan error, 2)
go s.streamUnidir(up, s.inbound, s.outbound, errs)
go s.streamUnidir(down, s.outbound, s.inbound, errs)
return <-errs
}
func (s *session) streamUnidir(dir direction, r, w net.Conn, errs chan error) {
for {
// Read from one connection
pkt, err := packets.ReadPacket(r)
if err != nil {
errs <- err
return
}
if dir == up {
if err := s.authorize(pkt); err != nil {
errs <- err
return
}
}
// Send to another
if err := pkt.Write(w); err != nil {
errs <- err
return
}
if dir == up {
s.notify(pkt)
}
}
}
func (s *session) authorize(pkt packets.ControlPacket) error {
switch p := pkt.(type) {
case *packets.ConnectPacket:
if err := s.event.AuthRegister(&p.Username, &p.ClientIdentifier, &p.Password); err != nil {
return err
}
s.client.username = p.Username
s.client.password = p.Password
s.client.ID = p.ClientIdentifier
return nil
case *packets.PublishPacket:
return s.event.AuthPublish(s.client.username, s.client.ID, &p.TopicName, &p.Payload)
case *packets.SubscribePacket:
return s.event.AuthSubscribe(s.client.username, s.client.ID, &p.Topics)
default:
return nil
}
}
func (s *session) notify(pkt packets.ControlPacket) {
switch p := pkt.(type) {
case *packets.ConnectPacket:
s.event.Register(s.client.ID)
case *packets.PublishPacket:
s.event.Publish(s.client.ID, p.TopicName, p.Payload)
case *packets.SubscribePacket:
s.event.Subscribe(s.client.ID, p.Topics)
case *packets.UnsubscribePacket:
s.event.Unsubscribe(s.client.ID, p.Topics)
default:
return
}
}

View File

@ -1,21 +0,0 @@
language: go
sudo: false
go:
- 1.11.x
- 1.10.x
- 1.9.x
install:
- go get -t ./...
- go get github.com/nats-io/gnatsd
- go get github.com/mattn/goveralls
- go get github.com/wadey/gocovmerge
- go get -u honnef.co/go/tools/cmd/megacheck
- go get -u github.com/client9/misspell/cmd/misspell
before_script:
- $(exit $(go fmt ./... | wc -l))
- go vet ./...
- misspell -error -locale US .
- megacheck -ignore "$(cat staticcheck.ignore)" ./...
script:
- go test -i -race ./...
- if [[ "$TRAVIS_GO_VERSION" == 1.11.* ]]; then ./scripts/cov.sh TRAVIS; else go test -v -race ./...; fi

View File

@ -1,4 +0,0 @@
github.com/nats-io/go-nats/*_test.go:SA2002
github.com/nats-io/go-nats/*/*_test.go:SA2002
github.com/nats-io/go-nats/test/context_test.go:SA1012
github.com/nats-io/go-nats/nats.go:SA6000

16
vendor/github.com/nats-io/jwt/.gitignore generated vendored Normal file
View File

@ -0,0 +1,16 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# IDE Files
.vscode
.idea/

22
vendor/github.com/nats-io/jwt/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,22 @@
language: go
sudo: false
go:
- 1.13.x
- 1.12.x
install:
- go get -t ./...
- go get github.com/mattn/goveralls
- go get github.com/wadey/gocovmerge
- go get -u honnef.co/go/tools/cmd/staticcheck
- go get -u github.com/client9/misspell/cmd/misspell
before_script:
- $(exit $(go fmt ./... | wc -l))
- go vet ./...
- misspell -error -locale US .
- staticcheck ./...
script:
- go test -v -race ./...
- if [[ "$TRAVIS_GO_VERSION" =~ 1.12 ]]; then ./scripts/cov.sh TRAVIS; fi

19
vendor/github.com/nats-io/jwt/Makefile generated vendored Normal file
View File

@ -0,0 +1,19 @@
.PHONY: test cover
build:
go build
test:
gofmt -s -w *.go
goimports -w *.go
go vet ./...
go test -v
go test -v --race
staticcheck ./...
fmt:
gofmt -w -s *.go
cover:
go test -v -covermode=count -coverprofile=coverage.out
go tool cover -html=coverage.out

54
vendor/github.com/nats-io/jwt/README.md generated vendored Normal file
View File

@ -0,0 +1,54 @@
# JWT
A [JWT](https://jwt.io/) implementation that uses [nkeys](https://github.com/nats-io/nkeys) to digitally sign JWT tokens.
Nkeys use [Ed25519](https://ed25519.cr.yp.to/) to provide authentication of JWT claims.
[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
[![ReportCard](http://goreportcard.com/badge/nats-io/jwt)](http://goreportcard.com/report/nats-io/jwt)
[![Build Status](https://travis-ci.org/nats-io/jwt.svg?branch=master)](http://travis-ci.org/nats-io/jwt)
[![GoDoc](http://godoc.org/github.com/nats-io/jwt?status.png)](http://godoc.org/github.com/nats-io/jwt)
[![Coverage Status](https://coveralls.io/repos/github/nats-io/jwt/badge.svg?branch=master&t=NmEFup)](https://coveralls.io/github/nats-io/jwt?branch=master)
```go
// Need a private key to sign the claim, nkeys makes it easy to create
kp, err := nkeys.CreateAccount()
if err != nil {
t.Fatal("unable to create account key", err)
}
pk, err := kp.PublicKey()
if err != nil {
t.Fatal("error getting public key", err)
}
// create a new claim
claims := NewAccountClaims(pk)
claims.Expires = time.Now().Add(time.Duration(time.Hour)).Unix()
// add details by modifying claims.Account
// serialize the claim to a JWT token
token, err := claims.Encode(kp)
if err != nil {
t.Fatal("error encoding token", err)
}
// on the receiving side, decode the token
c, err := DecodeAccountClaims(token)
if err != nil {
t.Fatal(err)
}
// if the token was decoded, it means that it
// validated and it wasn't tampered. the remaining and
// required test is to insure the issuer is trusted
pk, err := kp.PublicKey()
if err != nil {
t.Fatalf("unable to read public key: %v", err)
}
if c.Issuer != pk {
t.Fatalf("the public key is not trusted")
}
```

5
vendor/github.com/nats-io/jwt/ReleaseNotes.md generated vendored Normal file
View File

@ -0,0 +1,5 @@
# Release Notes
## 0.3.0
* Removed revocation claims in favor of timestamp-based revocation maps in account and export claims.

222
vendor/github.com/nats-io/jwt/account_claims.go generated vendored Normal file
View File

@ -0,0 +1,222 @@
/*
* Copyright 2018-2019 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"errors"
"sort"
"time"
"github.com/nats-io/nkeys"
)
// NoLimit is used to indicate a limit field is unlimited in value.
const NoLimit = -1
// OperatorLimits are used to limit access by an account
type OperatorLimits struct {
Subs int64 `json:"subs,omitempty"` // Max number of subscriptions
Conn int64 `json:"conn,omitempty"` // Max number of active connections
LeafNodeConn int64 `json:"leaf,omitempty"` // Max number of active leaf node connections
Imports int64 `json:"imports,omitempty"` // Max number of imports
Exports int64 `json:"exports,omitempty"` // Max number of exports
Data int64 `json:"data,omitempty"` // Max number of bytes
Payload int64 `json:"payload,omitempty"` // Max message payload
WildcardExports bool `json:"wildcards,omitempty"` // Are wildcards allowed in exports
}
// IsEmpty returns true if all of the limits are 0/false.
func (o *OperatorLimits) IsEmpty() bool {
return *o == OperatorLimits{}
}
// IsUnlimited returns true if all limits are
func (o *OperatorLimits) IsUnlimited() bool {
return *o == OperatorLimits{NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, true}
}
// Validate checks that the operator limits contain valid values
func (o *OperatorLimits) Validate(vr *ValidationResults) {
// negative values mean unlimited, so all numbers are valid
}
// Account holds account specific claims data
type Account struct {
Imports Imports `json:"imports,omitempty"`
Exports Exports `json:"exports,omitempty"`
Identities []Identity `json:"identity,omitempty"`
Limits OperatorLimits `json:"limits,omitempty"`
SigningKeys StringList `json:"signing_keys,omitempty"`
Revocations RevocationList `json:"revocations,omitempty"`
}
// Validate checks if the account is valid, based on the wrapper
func (a *Account) Validate(acct *AccountClaims, vr *ValidationResults) {
a.Imports.Validate(acct.Subject, vr)
a.Exports.Validate(vr)
a.Limits.Validate(vr)
for _, i := range a.Identities {
i.Validate(vr)
}
if !a.Limits.IsEmpty() && a.Limits.Imports >= 0 && int64(len(a.Imports)) > a.Limits.Imports {
vr.AddError("the account contains more imports than allowed by the operator")
}
// Check Imports and Exports for limit violations.
if a.Limits.Imports != NoLimit {
if int64(len(a.Imports)) > a.Limits.Imports {
vr.AddError("the account contains more imports than allowed by the operator")
}
}
if a.Limits.Exports != NoLimit {
if int64(len(a.Exports)) > a.Limits.Exports {
vr.AddError("the account contains more exports than allowed by the operator")
}
// Check for wildcard restrictions
if !a.Limits.WildcardExports {
for _, ex := range a.Exports {
if ex.Subject.HasWildCards() {
vr.AddError("the account contains wildcard exports that are not allowed by the operator")
}
}
}
}
for _, k := range a.SigningKeys {
if !nkeys.IsValidPublicAccountKey(k) {
vr.AddError("%s is not an account public key", k)
}
}
}
// AccountClaims defines the body of an account JWT
type AccountClaims struct {
ClaimsData
Account `json:"nats,omitempty"`
}
// NewAccountClaims creates a new account JWT
func NewAccountClaims(subject string) *AccountClaims {
if subject == "" {
return nil
}
c := &AccountClaims{}
// Set to unlimited to start. We do it this way so we get compiler
// errors if we add to the OperatorLimits.
c.Limits = OperatorLimits{NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, true}
c.Subject = subject
return c
}
// Encode converts account claims into a JWT string
func (a *AccountClaims) Encode(pair nkeys.KeyPair) (string, error) {
if !nkeys.IsValidPublicAccountKey(a.Subject) {
return "", errors.New("expected subject to be account public key")
}
sort.Sort(a.Exports)
sort.Sort(a.Imports)
a.ClaimsData.Type = AccountClaim
return a.ClaimsData.Encode(pair, a)
}
// DecodeAccountClaims decodes account claims from a JWT string
func DecodeAccountClaims(token string) (*AccountClaims, error) {
v := AccountClaims{}
if err := Decode(token, &v); err != nil {
return nil, err
}
return &v, nil
}
func (a *AccountClaims) String() string {
return a.ClaimsData.String(a)
}
// Payload pulls the accounts specific payload out of the claims
func (a *AccountClaims) Payload() interface{} {
return &a.Account
}
// Validate checks the accounts contents
func (a *AccountClaims) Validate(vr *ValidationResults) {
a.ClaimsData.Validate(vr)
a.Account.Validate(a, vr)
if nkeys.IsValidPublicAccountKey(a.ClaimsData.Issuer) {
if len(a.Identities) > 0 {
vr.AddWarning("self-signed account JWTs shouldn't contain identity proofs")
}
if !a.Limits.IsEmpty() {
vr.AddWarning("self-signed account JWTs shouldn't contain operator limits")
}
}
}
// ExpectedPrefixes defines the types that can encode an account jwt, account and operator
func (a *AccountClaims) ExpectedPrefixes() []nkeys.PrefixByte {
return []nkeys.PrefixByte{nkeys.PrefixByteAccount, nkeys.PrefixByteOperator}
}
// Claims returns the accounts claims data
func (a *AccountClaims) Claims() *ClaimsData {
return &a.ClaimsData
}
// DidSign checks the claims against the account's public key and its signing keys
func (a *AccountClaims) DidSign(op Claims) bool {
if op != nil {
issuer := op.Claims().Issuer
if issuer == a.Subject {
return true
}
return a.SigningKeys.Contains(issuer)
}
return false
}
// Revoke enters a revocation by publickey using time.Now().
func (a *AccountClaims) Revoke(pubKey string) {
a.RevokeAt(pubKey, time.Now())
}
// RevokeAt enters a revocation by publickey and timestamp into this export
// If there is already a revocation for this public key that is newer, it is kept.
func (a *AccountClaims) RevokeAt(pubKey string, timestamp time.Time) {
if a.Revocations == nil {
a.Revocations = RevocationList{}
}
a.Revocations.Revoke(pubKey, timestamp)
}
// ClearRevocation removes any revocation for the public key
func (a *AccountClaims) ClearRevocation(pubKey string) {
a.Revocations.ClearRevocation(pubKey)
}
// IsRevokedAt checks if the public key is in the revoked list with a timestamp later than
// the one passed in. Generally this method is called with time.Now() but other time's can
// be used for testing.
func (a *AccountClaims) IsRevokedAt(pubKey string, timestamp time.Time) bool {
return a.Revocations.IsRevoked(pubKey, timestamp)
}
// IsRevoked checks if the public key is in the revoked list with time.Now()
func (a *AccountClaims) IsRevoked(pubKey string) bool {
return a.Revocations.IsRevoked(pubKey, time.Now())
}

166
vendor/github.com/nats-io/jwt/activation_claims.go generated vendored Normal file
View File

@ -0,0 +1,166 @@
/*
* Copyright 2018 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"crypto/sha256"
"encoding/base32"
"errors"
"fmt"
"strings"
"github.com/nats-io/nkeys"
)
// Activation defines the custom parts of an activation claim
type Activation struct {
ImportSubject Subject `json:"subject,omitempty"`
ImportType ExportType `json:"type,omitempty"`
Limits
}
// IsService returns true if an Activation is for a service
func (a *Activation) IsService() bool {
return a.ImportType == Service
}
// IsStream returns true if an Activation is for a stream
func (a *Activation) IsStream() bool {
return a.ImportType == Stream
}
// Validate checks the exports and limits in an activation JWT
func (a *Activation) Validate(vr *ValidationResults) {
if !a.IsService() && !a.IsStream() {
vr.AddError("invalid export type: %q", a.ImportType)
}
if a.IsService() {
if a.ImportSubject.HasWildCards() {
vr.AddError("services cannot have wildcard subject: %q", a.ImportSubject)
}
}
a.ImportSubject.Validate(vr)
a.Limits.Validate(vr)
}
// ActivationClaims holds the data specific to an activation JWT
type ActivationClaims struct {
ClaimsData
Activation `json:"nats,omitempty"`
// IssuerAccount stores the public key for the account the issuer represents.
// When set, the claim was issued by a signing key.
IssuerAccount string `json:"issuer_account,omitempty"`
}
// NewActivationClaims creates a new activation claim with the provided sub
func NewActivationClaims(subject string) *ActivationClaims {
if subject == "" {
return nil
}
ac := &ActivationClaims{}
ac.Subject = subject
return ac
}
// Encode turns an activation claim into a JWT strimg
func (a *ActivationClaims) Encode(pair nkeys.KeyPair) (string, error) {
if !nkeys.IsValidPublicAccountKey(a.ClaimsData.Subject) {
return "", errors.New("expected subject to be an account")
}
a.ClaimsData.Type = ActivationClaim
return a.ClaimsData.Encode(pair, a)
}
// DecodeActivationClaims tries to create an activation claim from a JWT string
func DecodeActivationClaims(token string) (*ActivationClaims, error) {
v := ActivationClaims{}
if err := Decode(token, &v); err != nil {
return nil, err
}
return &v, nil
}
// Payload returns the activation specific part of the JWT
func (a *ActivationClaims) Payload() interface{} {
return a.Activation
}
// Validate checks the claims
func (a *ActivationClaims) Validate(vr *ValidationResults) {
a.ClaimsData.Validate(vr)
a.Activation.Validate(vr)
if a.IssuerAccount != "" && !nkeys.IsValidPublicAccountKey(a.IssuerAccount) {
vr.AddError("account_id is not an account public key")
}
}
// ExpectedPrefixes defines the types that can sign an activation jwt, account and oeprator
func (a *ActivationClaims) ExpectedPrefixes() []nkeys.PrefixByte {
return []nkeys.PrefixByte{nkeys.PrefixByteAccount, nkeys.PrefixByteOperator}
}
// Claims returns the generic part of the JWT
func (a *ActivationClaims) Claims() *ClaimsData {
return &a.ClaimsData
}
func (a *ActivationClaims) String() string {
return a.ClaimsData.String(a)
}
// HashID returns a hash of the claims that can be used to identify it.
// The hash is calculated by creating a string with
// issuerPubKey.subjectPubKey.<subject> and constructing the sha-256 hash and base32 encoding that.
// <subject> is the exported subject, minus any wildcards, so foo.* becomes foo.
// the one special case is that if the export start with "*" or is ">" the <subject> "_"
func (a *ActivationClaims) HashID() (string, error) {
if a.Issuer == "" || a.Subject == "" || a.ImportSubject == "" {
return "", fmt.Errorf("not enough data in the activaion claims to create a hash")
}
subject := cleanSubject(string(a.ImportSubject))
base := fmt.Sprintf("%s.%s.%s", a.Issuer, a.Subject, subject)
h := sha256.New()
h.Write([]byte(base))
sha := h.Sum(nil)
hash := base32.StdEncoding.EncodeToString(sha)
return hash, nil
}
func cleanSubject(subject string) string {
split := strings.Split(subject, ".")
cleaned := ""
for i, tok := range split {
if tok == "*" || tok == ">" {
if i == 0 {
cleaned = "_"
break
}
cleaned = strings.Join(split[:i], ".")
break
}
}
if cleaned == "" {
cleaned = subject
}
return cleaned
}

302
vendor/github.com/nats-io/jwt/claims.go generated vendored Normal file
View File

@ -0,0 +1,302 @@
/*
* Copyright 2018-2019 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"crypto/sha512"
"encoding/base32"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"github.com/nats-io/nkeys"
)
// ClaimType is used to indicate the type of JWT being stored in a Claim
type ClaimType string
const (
// AccountClaim is the type of an Account JWT
AccountClaim = "account"
//ActivationClaim is the type of an activation JWT
ActivationClaim = "activation"
//UserClaim is the type of an user JWT
UserClaim = "user"
//ServerClaim is the type of an server JWT
ServerClaim = "server"
//ClusterClaim is the type of an cluster JWT
ClusterClaim = "cluster"
//OperatorClaim is the type of an operator JWT
OperatorClaim = "operator"
)
// Claims is a JWT claims
type Claims interface {
Claims() *ClaimsData
Encode(kp nkeys.KeyPair) (string, error)
ExpectedPrefixes() []nkeys.PrefixByte
Payload() interface{}
String() string
Validate(vr *ValidationResults)
Verify(payload string, sig []byte) bool
}
// ClaimsData is the base struct for all claims
type ClaimsData struct {
Audience string `json:"aud,omitempty"`
Expires int64 `json:"exp,omitempty"`
ID string `json:"jti,omitempty"`
IssuedAt int64 `json:"iat,omitempty"`
Issuer string `json:"iss,omitempty"`
Name string `json:"name,omitempty"`
NotBefore int64 `json:"nbf,omitempty"`
Subject string `json:"sub,omitempty"`
Tags TagList `json:"tags,omitempty"`
Type ClaimType `json:"type,omitempty"`
}
// Prefix holds the prefix byte for an NKey
type Prefix struct {
nkeys.PrefixByte
}
func encodeToString(d []byte) string {
return base64.RawURLEncoding.EncodeToString(d)
}
func decodeString(s string) ([]byte, error) {
return base64.RawURLEncoding.DecodeString(s)
}
func serialize(v interface{}) (string, error) {
j, err := json.Marshal(v)
if err != nil {
return "", err
}
return encodeToString(j), nil
}
func (c *ClaimsData) doEncode(header *Header, kp nkeys.KeyPair, claim Claims) (string, error) {
if header == nil {
return "", errors.New("header is required")
}
if kp == nil {
return "", errors.New("keypair is required")
}
if c.Subject == "" {
return "", errors.New("subject is not set")
}
h, err := serialize(header)
if err != nil {
return "", err
}
issuerBytes, err := kp.PublicKey()
if err != nil {
return "", err
}
prefixes := claim.ExpectedPrefixes()
if prefixes != nil {
ok := false
for _, p := range prefixes {
switch p {
case nkeys.PrefixByteAccount:
if nkeys.IsValidPublicAccountKey(issuerBytes) {
ok = true
}
case nkeys.PrefixByteOperator:
if nkeys.IsValidPublicOperatorKey(issuerBytes) {
ok = true
}
case nkeys.PrefixByteServer:
if nkeys.IsValidPublicServerKey(issuerBytes) {
ok = true
}
case nkeys.PrefixByteCluster:
if nkeys.IsValidPublicClusterKey(issuerBytes) {
ok = true
}
case nkeys.PrefixByteUser:
if nkeys.IsValidPublicUserKey(issuerBytes) {
ok = true
}
}
}
if !ok {
return "", fmt.Errorf("unable to validate expected prefixes - %v", prefixes)
}
}
c.Issuer = string(issuerBytes)
c.IssuedAt = time.Now().UTC().Unix()
c.ID, err = c.hash()
if err != nil {
return "", err
}
payload, err := serialize(claim)
if err != nil {
return "", err
}
sig, err := kp.Sign([]byte(payload))
if err != nil {
return "", err
}
eSig := encodeToString(sig)
return fmt.Sprintf("%s.%s.%s", h, payload, eSig), nil
}
func (c *ClaimsData) hash() (string, error) {
j, err := json.Marshal(c)
if err != nil {
return "", err
}
h := sha512.New512_256()
h.Write(j)
return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(h.Sum(nil)), nil
}
// Encode encodes a claim into a JWT token. The claim is signed with the
// provided nkey's private key
func (c *ClaimsData) Encode(kp nkeys.KeyPair, payload Claims) (string, error) {
return c.doEncode(&Header{TokenTypeJwt, AlgorithmNkey}, kp, payload)
}
// Returns a JSON representation of the claim
func (c *ClaimsData) String(claim interface{}) string {
j, err := json.MarshalIndent(claim, "", " ")
if err != nil {
return ""
}
return string(j)
}
func parseClaims(s string, target Claims) error {
h, err := decodeString(s)
if err != nil {
return err
}
return json.Unmarshal(h, &target)
}
// Verify verifies that the encoded payload was signed by the
// provided public key. Verify is called automatically with
// the claims portion of the token and the public key in the claim.
// Client code need to insure that the public key in the
// claim is trusted.
func (c *ClaimsData) Verify(payload string, sig []byte) bool {
// decode the public key
kp, err := nkeys.FromPublicKey(c.Issuer)
if err != nil {
return false
}
if err := kp.Verify([]byte(payload), sig); err != nil {
return false
}
return true
}
// Validate checks a claim to make sure it is valid. Validity checks
// include expiration and not before constraints.
func (c *ClaimsData) Validate(vr *ValidationResults) {
now := time.Now().UTC().Unix()
if c.Expires > 0 && now > c.Expires {
vr.AddTimeCheck("claim is expired")
}
if c.NotBefore > 0 && c.NotBefore > now {
vr.AddTimeCheck("claim is not yet valid")
}
}
// IsSelfSigned returns true if the claims issuer is the subject
func (c *ClaimsData) IsSelfSigned() bool {
return c.Issuer == c.Subject
}
// Decode takes a JWT string decodes it and validates it
// and return the embedded Claims. If the token header
// doesn't match the expected algorithm, or the claim is
// not valid or verification fails an error is returned.
func Decode(token string, target Claims) error {
// must have 3 chunks
chunks := strings.Split(token, ".")
if len(chunks) != 3 {
return errors.New("expected 3 chunks")
}
_, err := parseHeaders(chunks[0])
if err != nil {
return err
}
if err := parseClaims(chunks[1], target); err != nil {
return err
}
sig, err := decodeString(chunks[2])
if err != nil {
return err
}
if !target.Verify(chunks[1], sig) {
return errors.New("claim failed signature verification")
}
prefixes := target.ExpectedPrefixes()
if prefixes != nil {
ok := false
issuer := target.Claims().Issuer
for _, p := range prefixes {
switch p {
case nkeys.PrefixByteAccount:
if nkeys.IsValidPublicAccountKey(issuer) {
ok = true
}
case nkeys.PrefixByteOperator:
if nkeys.IsValidPublicOperatorKey(issuer) {
ok = true
}
case nkeys.PrefixByteServer:
if nkeys.IsValidPublicServerKey(issuer) {
ok = true
}
case nkeys.PrefixByteCluster:
if nkeys.IsValidPublicClusterKey(issuer) {
ok = true
}
case nkeys.PrefixByteUser:
if nkeys.IsValidPublicUserKey(issuer) {
ok = true
}
}
}
if !ok {
return fmt.Errorf("unable to validate expected prefixes - %v", prefixes)
}
}
return nil
}

94
vendor/github.com/nats-io/jwt/cluster_claims.go generated vendored Normal file
View File

@ -0,0 +1,94 @@
/*
* Copyright 2018 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"errors"
"github.com/nats-io/nkeys"
)
// Cluster stores the cluster specific elements of a cluster JWT
type Cluster struct {
Trust []string `json:"identity,omitempty"`
Accounts []string `json:"accts,omitempty"`
AccountURL string `json:"accturl,omitempty"`
OperatorURL string `json:"opurl,omitempty"`
}
// Validate checks the cluster and permissions for a cluster JWT
func (c *Cluster) Validate(vr *ValidationResults) {
// fixme validate cluster data
}
// ClusterClaims defines the data in a cluster JWT
type ClusterClaims struct {
ClaimsData
Cluster `json:"nats,omitempty"`
}
// NewClusterClaims creates a new cluster JWT with the specified subject/public key
func NewClusterClaims(subject string) *ClusterClaims {
if subject == "" {
return nil
}
c := &ClusterClaims{}
c.Subject = subject
return c
}
// Encode tries to turn the cluster claims into a JWT string
func (c *ClusterClaims) Encode(pair nkeys.KeyPair) (string, error) {
if !nkeys.IsValidPublicClusterKey(c.Subject) {
return "", errors.New("expected subject to be a cluster public key")
}
c.ClaimsData.Type = ClusterClaim
return c.ClaimsData.Encode(pair, c)
}
// DecodeClusterClaims tries to parse cluster claims from a JWT string
func DecodeClusterClaims(token string) (*ClusterClaims, error) {
v := ClusterClaims{}
if err := Decode(token, &v); err != nil {
return nil, err
}
return &v, nil
}
func (c *ClusterClaims) String() string {
return c.ClaimsData.String(c)
}
// Payload returns the cluster specific data
func (c *ClusterClaims) Payload() interface{} {
return &c.Cluster
}
// Validate checks the generic and cluster data in the cluster claims
func (c *ClusterClaims) Validate(vr *ValidationResults) {
c.ClaimsData.Validate(vr)
c.Cluster.Validate(vr)
}
// ExpectedPrefixes defines the types that can encode a cluster JWT, operator or cluster
func (c *ClusterClaims) ExpectedPrefixes() []nkeys.PrefixByte {
return []nkeys.PrefixByte{nkeys.PrefixByteOperator, nkeys.PrefixByteCluster}
}
// Claims returns the generic data
func (c *ClusterClaims) Claims() *ClaimsData {
return &c.ClaimsData
}

203
vendor/github.com/nats-io/jwt/creds_utils.go generated vendored Normal file
View File

@ -0,0 +1,203 @@
package jwt
import (
"bytes"
"errors"
"fmt"
"regexp"
"strings"
"github.com/nats-io/nkeys"
)
// DecorateJWT returns a decorated JWT that describes the kind of JWT
func DecorateJWT(jwtString string) ([]byte, error) {
gc, err := DecodeGeneric(jwtString)
if err != nil {
return nil, err
}
return formatJwt(string(gc.Type), jwtString)
}
func formatJwt(kind string, jwtString string) ([]byte, error) {
templ := `-----BEGIN NATS %s JWT-----
%s
------END NATS %s JWT------
`
w := bytes.NewBuffer(nil)
kind = strings.ToUpper(kind)
_, err := fmt.Fprintf(w, templ, kind, jwtString, kind)
if err != nil {
return nil, err
}
return w.Bytes(), nil
}
// DecorateSeed takes a seed and returns a string that wraps
// the seed in the form:
// ************************* IMPORTANT *************************
// NKEY Seed printed below can be used sign and prove identity.
// NKEYs are sensitive and should be treated as secrets.
//
// -----BEGIN USER NKEY SEED-----
// SUAIO3FHUX5PNV2LQIIP7TZ3N4L7TX3W53MQGEIVYFIGA635OZCKEYHFLM
// ------END USER NKEY SEED------
func DecorateSeed(seed []byte) ([]byte, error) {
w := bytes.NewBuffer(nil)
ts := bytes.TrimSpace(seed)
pre := string(ts[0:2])
kind := ""
switch pre {
case "SU":
kind = "USER"
case "SA":
kind = "ACCOUNT"
case "SO":
kind = "OPERATOR"
default:
return nil, errors.New("seed is not an operator, account or user seed")
}
header := `************************* IMPORTANT *************************
NKEY Seed printed below can be used to sign and prove identity.
NKEYs are sensitive and should be treated as secrets.
-----BEGIN %s NKEY SEED-----
`
_, err := fmt.Fprintf(w, header, kind)
if err != nil {
return nil, err
}
w.Write(ts)
footer := `
------END %s NKEY SEED------
*************************************************************
`
_, err = fmt.Fprintf(w, footer, kind)
if err != nil {
return nil, err
}
return w.Bytes(), nil
}
var userConfigRE = regexp.MustCompile(`\s*(?:(?:[-]{3,}[^\n]*[-]{3,}\n)(.+)(?:\n\s*[-]{3,}[^\n]*[-]{3,}\n))`)
// An user config file looks like this:
// -----BEGIN NATS USER JWT-----
// eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5...
// ------END NATS USER JWT------
//
// ************************* IMPORTANT *************************
// NKEY Seed printed below can be used sign and prove identity.
// NKEYs are sensitive and should be treated as secrets.
//
// -----BEGIN USER NKEY SEED-----
// SUAIO3FHUX5PNV2LQIIP7TZ3N4L7TX3W53MQGEIVYFIGA635OZCKEYHFLM
// ------END USER NKEY SEED------
// FormatUserConfig returns a decorated file with a decorated JWT and decorated seed
func FormatUserConfig(jwtString string, seed []byte) ([]byte, error) {
gc, err := DecodeGeneric(jwtString)
if err != nil {
return nil, err
}
if gc.Type != UserClaim {
return nil, fmt.Errorf("%q cannot be serialized as a user config", string(gc.Type))
}
w := bytes.NewBuffer(nil)
jd, err := formatJwt(string(gc.Type), jwtString)
if err != nil {
return nil, err
}
_, err = w.Write(jd)
if err != nil {
return nil, err
}
if !bytes.HasPrefix(bytes.TrimSpace(seed), []byte("SU")) {
return nil, fmt.Errorf("nkey seed is not an user seed")
}
d, err := DecorateSeed(seed)
if err != nil {
return nil, err
}
_, err = w.Write(d)
if err != nil {
return nil, err
}
return w.Bytes(), nil
}
// ParseDecoratedJWT takes a creds file and returns the JWT portion.
func ParseDecoratedJWT(contents []byte) (string, error) {
items := userConfigRE.FindAllSubmatch(contents, -1)
if len(items) == 0 {
return string(contents), nil
}
// First result should be the user JWT.
// We copy here so that if the file contained a seed file too we wipe appropriately.
raw := items[0][1]
tmp := make([]byte, len(raw))
copy(tmp, raw)
return string(tmp), nil
}
// ParseDecoratedNKey takes a creds file, finds the NKey portion and creates a
// key pair from it.
func ParseDecoratedNKey(contents []byte) (nkeys.KeyPair, error) {
var seed []byte
items := userConfigRE.FindAllSubmatch(contents, -1)
if len(items) > 1 {
seed = items[1][1]
} else {
lines := bytes.Split(contents, []byte("\n"))
for _, line := range lines {
if bytes.HasPrefix(bytes.TrimSpace(line), []byte("SO")) ||
bytes.HasPrefix(bytes.TrimSpace(line), []byte("SA")) ||
bytes.HasPrefix(bytes.TrimSpace(line), []byte("SU")) {
seed = line
break
}
}
}
if seed == nil {
return nil, errors.New("no nkey seed found")
}
if !bytes.HasPrefix(seed, []byte("SO")) &&
!bytes.HasPrefix(seed, []byte("SA")) &&
!bytes.HasPrefix(seed, []byte("SU")) {
return nil, errors.New("doesn't contain a seed nkey")
}
kp, err := nkeys.FromSeed(seed)
if err != nil {
return nil, err
}
return kp, nil
}
// ParseDecoratedUserNKey takes a creds file, finds the NKey portion and creates a
// key pair from it. Similar to ParseDecoratedNKey but fails for non-user keys.
func ParseDecoratedUserNKey(contents []byte) (nkeys.KeyPair, error) {
nk, err := ParseDecoratedNKey(contents)
if err != nil {
return nil, err
}
seed, err := nk.Seed()
if err != nil {
return nil, err
}
if !bytes.HasPrefix(seed, []byte("SU")) {
return nil, errors.New("doesn't contain an user seed nkey")
}
kp, err := nkeys.FromSeed(seed)
if err != nil {
return nil, err
}
return kp, nil
}

236
vendor/github.com/nats-io/jwt/exports.go generated vendored Normal file
View File

@ -0,0 +1,236 @@
/*
* Copyright 2018-2019 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"fmt"
"time"
)
// ResponseType is used to store an export response type
type ResponseType string
const (
// ResponseTypeSingleton is used for a service that sends a single response only
ResponseTypeSingleton = "Singleton"
// ResponseTypeStream is used for a service that will send multiple responses
ResponseTypeStream = "Stream"
// ResponseTypeChunked is used for a service that sends a single response in chunks (so not quite a stream)
ResponseTypeChunked = "Chunked"
)
// ServiceLatency is used when observing and exported service for
// latency measurements.
// Sampling 1-100, represents sampling rate, defaults to 100.
// Results is the subject where the latency metrics are published.
// A metric will be defined by the nats-server's ServiceLatency. Time durations
// are in nanoseconds.
// see https://github.com/nats-io/nats-server/blob/master/server/accounts.go#L524
// e.g.
// {
// "app": "dlc22",
// "start": "2019-09-16T21:46:23.636869585-07:00",
// "svc": 219732,
// "nats": {
// "req": 320415,
// "resp": 228268,
// "sys": 0
// },
// "total": 768415
// }
//
type ServiceLatency struct {
Sampling int `json:"sampling,omitempty"`
Results Subject `json:"results"`
}
func (sl *ServiceLatency) Validate(vr *ValidationResults) {
if sl.Sampling < 1 || sl.Sampling > 100 {
vr.AddError("sampling percentage needs to be between 1-100")
}
sl.Results.Validate(vr)
if sl.Results.HasWildCards() {
vr.AddError("results subject can not contain wildcards")
}
}
// Export represents a single export
type Export struct {
Name string `json:"name,omitempty"`
Subject Subject `json:"subject,omitempty"`
Type ExportType `json:"type,omitempty"`
TokenReq bool `json:"token_req,omitempty"`
Revocations RevocationList `json:"revocations,omitempty"`
ResponseType ResponseType `json:"response_type,omitempty"`
Latency *ServiceLatency `json:"service_latency,omitempty"`
}
// IsService returns true if an export is for a service
func (e *Export) IsService() bool {
return e.Type == Service
}
// IsStream returns true if an export is for a stream
func (e *Export) IsStream() bool {
return e.Type == Stream
}
// IsSingleResponse returns true if an export has a single response
// or no resopnse type is set, also checks that the type is service
func (e *Export) IsSingleResponse() bool {
return e.Type == Service && (e.ResponseType == ResponseTypeSingleton || e.ResponseType == "")
}
// IsChunkedResponse returns true if an export has a chunked response
func (e *Export) IsChunkedResponse() bool {
return e.Type == Service && e.ResponseType == ResponseTypeChunked
}
// IsStreamResponse returns true if an export has a chunked response
func (e *Export) IsStreamResponse() bool {
return e.Type == Service && e.ResponseType == ResponseTypeStream
}
// Validate appends validation issues to the passed in results list
func (e *Export) Validate(vr *ValidationResults) {
if !e.IsService() && !e.IsStream() {
vr.AddError("invalid export type: %q", e.Type)
}
if e.IsService() && !e.IsSingleResponse() && !e.IsChunkedResponse() && !e.IsStreamResponse() {
vr.AddError("invalid response type for service: %q", e.ResponseType)
}
if e.IsStream() && e.ResponseType != "" {
vr.AddError("invalid response type for stream: %q", e.ResponseType)
}
if e.Latency != nil {
if !e.IsService() {
vr.AddError("latency tracking only permitted for services")
}
e.Latency.Validate(vr)
}
e.Subject.Validate(vr)
}
// Revoke enters a revocation by publickey using time.Now().
func (e *Export) Revoke(pubKey string) {
e.RevokeAt(pubKey, time.Now())
}
// RevokeAt enters a revocation by publickey and timestamp into this export
// If there is already a revocation for this public key that is newer, it is kept.
func (e *Export) RevokeAt(pubKey string, timestamp time.Time) {
if e.Revocations == nil {
e.Revocations = RevocationList{}
}
e.Revocations.Revoke(pubKey, timestamp)
}
// ClearRevocation removes any revocation for the public key
func (e *Export) ClearRevocation(pubKey string) {
e.Revocations.ClearRevocation(pubKey)
}
// IsRevokedAt checks if the public key is in the revoked list with a timestamp later than
// the one passed in. Generally this method is called with time.Now() but other time's can
// be used for testing.
func (e *Export) IsRevokedAt(pubKey string, timestamp time.Time) bool {
return e.Revocations.IsRevoked(pubKey, timestamp)
}
// IsRevoked checks if the public key is in the revoked list with time.Now()
func (e *Export) IsRevoked(pubKey string) bool {
return e.Revocations.IsRevoked(pubKey, time.Now())
}
// Exports is a slice of exports
type Exports []*Export
// Add appends exports to the list
func (e *Exports) Add(i ...*Export) {
*e = append(*e, i...)
}
func isContainedIn(kind ExportType, subjects []Subject, vr *ValidationResults) {
m := make(map[string]string)
for i, ns := range subjects {
for j, s := range subjects {
if i == j {
continue
}
if ns.IsContainedIn(s) {
str := string(s)
_, ok := m[str]
if !ok {
m[str] = string(ns)
}
}
}
}
if len(m) != 0 {
for k, v := range m {
var vi ValidationIssue
vi.Blocking = true
vi.Description = fmt.Sprintf("%s export subject %q already exports %q", kind, k, v)
vr.Add(&vi)
}
}
}
// Validate calls validate on all of the exports
func (e *Exports) Validate(vr *ValidationResults) error {
var serviceSubjects []Subject
var streamSubjects []Subject
for _, v := range *e {
if v.IsService() {
serviceSubjects = append(serviceSubjects, v.Subject)
} else {
streamSubjects = append(streamSubjects, v.Subject)
}
v.Validate(vr)
}
isContainedIn(Service, serviceSubjects, vr)
isContainedIn(Stream, streamSubjects, vr)
return nil
}
// HasExportContainingSubject checks if the export list has an export with the provided subject
func (e *Exports) HasExportContainingSubject(subject Subject) bool {
for _, s := range *e {
if subject.IsContainedIn(s.Subject) {
return true
}
}
return false
}
func (e Exports) Len() int {
return len(e)
}
func (e Exports) Swap(i, j int) {
e[i], e[j] = e[j], e[i]
}
func (e Exports) Less(i, j int) bool {
return e[i].Subject < e[j].Subject
}

73
vendor/github.com/nats-io/jwt/genericlaims.go generated vendored Normal file
View File

@ -0,0 +1,73 @@
/*
* Copyright 2018 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import "github.com/nats-io/nkeys"
// GenericClaims can be used to read a JWT as a map for any non-generic fields
type GenericClaims struct {
ClaimsData
Data map[string]interface{} `json:"nats,omitempty"`
}
// NewGenericClaims creates a map-based Claims
func NewGenericClaims(subject string) *GenericClaims {
if subject == "" {
return nil
}
c := GenericClaims{}
c.Subject = subject
c.Data = make(map[string]interface{})
return &c
}
// DecodeGeneric takes a JWT string and decodes it into a ClaimsData and map
func DecodeGeneric(token string) (*GenericClaims, error) {
v := GenericClaims{}
if err := Decode(token, &v); err != nil {
return nil, err
}
return &v, nil
}
// Claims returns the standard part of the generic claim
func (gc *GenericClaims) Claims() *ClaimsData {
return &gc.ClaimsData
}
// Payload returns the custom part of the claims data
func (gc *GenericClaims) Payload() interface{} {
return &gc.Data
}
// Encode takes a generic claims and creates a JWT string
func (gc *GenericClaims) Encode(pair nkeys.KeyPair) (string, error) {
return gc.ClaimsData.Encode(pair, gc)
}
// Validate checks the generic part of the claims data
func (gc *GenericClaims) Validate(vr *ValidationResults) {
gc.ClaimsData.Validate(vr)
}
func (gc *GenericClaims) String() string {
return gc.ClaimsData.String(gc)
}
// ExpectedPrefixes returns the types allowed to encode a generic JWT, which is nil for all
func (gc *GenericClaims) ExpectedPrefixes() []nkeys.PrefixByte {
return nil
}

3
vendor/github.com/nats-io/jwt/go.mod generated vendored Normal file
View File

@ -0,0 +1,3 @@
module github.com/nats-io/jwt
require github.com/nats-io/nkeys v0.1.3

9
vendor/github.com/nats-io/jwt/go.sum generated vendored Normal file
View File

@ -0,0 +1,9 @@
github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

71
vendor/github.com/nats-io/jwt/header.go generated vendored Normal file
View File

@ -0,0 +1,71 @@
/*
* Copyright 2018-2019 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"encoding/json"
"fmt"
"strings"
)
const (
// Version is semantic version.
Version = "0.3.2"
// TokenTypeJwt is the JWT token type supported JWT tokens
// encoded and decoded by this library
TokenTypeJwt = "jwt"
// AlgorithmNkey is the algorithm supported by JWT tokens
// encoded and decoded by this library
AlgorithmNkey = "ed25519"
)
// Header is a JWT Jose Header
type Header struct {
Type string `json:"typ"`
Algorithm string `json:"alg"`
}
// Parses a header JWT token
func parseHeaders(s string) (*Header, error) {
h, err := decodeString(s)
if err != nil {
return nil, err
}
header := Header{}
if err := json.Unmarshal(h, &header); err != nil {
return nil, err
}
if err := header.Valid(); err != nil {
return nil, err
}
return &header, nil
}
// Valid validates the Header. It returns nil if the Header is
// a JWT header, and the algorithm used is the NKEY algorithm.
func (h *Header) Valid() error {
if TokenTypeJwt != strings.ToLower(h.Type) {
return fmt.Errorf("not supported type %q", h.Type)
}
if AlgorithmNkey != strings.ToLower(h.Algorithm) {
return fmt.Errorf("unexpected %q algorithm", h.Algorithm)
}
return nil
}

151
vendor/github.com/nats-io/jwt/imports.go generated vendored Normal file
View File

@ -0,0 +1,151 @@
/*
* Copyright 2018-2019 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"io/ioutil"
"net/http"
"net/url"
"time"
)
// Import describes a mapping from another account into this one
type Import struct {
Name string `json:"name,omitempty"`
// Subject field in an import is always from the perspective of the
// initial publisher - in the case of a stream it is the account owning
// the stream (the exporter), and in the case of a service it is the
// account making the request (the importer).
Subject Subject `json:"subject,omitempty"`
Account string `json:"account,omitempty"`
Token string `json:"token,omitempty"`
// To field in an import is always from the perspective of the subscriber
// in the case of a stream it is the client of the stream (the importer),
// from the perspective of a service, it is the subscription waiting for
// requests (the exporter). If the field is empty, it will default to the
// value in the Subject field.
To Subject `json:"to,omitempty"`
Type ExportType `json:"type,omitempty"`
}
// IsService returns true if the import is of type service
func (i *Import) IsService() bool {
return i.Type == Service
}
// IsStream returns true if the import is of type stream
func (i *Import) IsStream() bool {
return i.Type == Stream
}
// Validate checks if an import is valid for the wrapping account
func (i *Import) Validate(actPubKey string, vr *ValidationResults) {
if !i.IsService() && !i.IsStream() {
vr.AddError("invalid import type: %q", i.Type)
}
if i.Account == "" {
vr.AddWarning("account to import from is not specified")
}
i.Subject.Validate(vr)
if i.IsService() && i.Subject.HasWildCards() {
vr.AddError("services cannot have wildcard subject: %q", i.Subject)
}
if i.IsStream() && i.To.HasWildCards() {
vr.AddError("streams cannot have wildcard to subject: %q", i.Subject)
}
var act *ActivationClaims
if i.Token != "" {
// Check to see if its an embedded JWT or a URL.
if url, err := url.Parse(i.Token); err == nil && url.Scheme != "" {
c := &http.Client{Timeout: 5 * time.Second}
resp, err := c.Get(url.String())
if err != nil {
vr.AddWarning("import %s contains an unreachable token URL %q", i.Subject, i.Token)
}
if resp != nil {
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
vr.AddWarning("import %s contains an unreadable token URL %q", i.Subject, i.Token)
} else {
act, err = DecodeActivationClaims(string(body))
if err != nil {
vr.AddWarning("import %s contains a url %q with an invalid activation token", i.Subject, i.Token)
}
}
}
} else {
var err error
act, err = DecodeActivationClaims(i.Token)
if err != nil {
vr.AddWarning("import %q contains an invalid activation token", i.Subject)
}
}
}
if act != nil {
if act.Issuer != i.Account {
vr.AddWarning("activation token doesn't match account for import %q", i.Subject)
}
if act.ClaimsData.Subject != actPubKey {
vr.AddWarning("activation token doesn't match account it is being included in, %q", i.Subject)
}
} else {
vr.AddWarning("no activation provided for import %s", i.Subject)
}
}
// Imports is a list of import structs
type Imports []*Import
// Validate checks if an import is valid for the wrapping account
func (i *Imports) Validate(acctPubKey string, vr *ValidationResults) {
toSet := make(map[Subject]bool, len(*i))
for _, v := range *i {
if v.Type == Service {
if _, ok := toSet[v.To]; ok {
vr.AddError("Duplicate To subjects for %q", v.To)
}
toSet[v.To] = true
}
v.Validate(acctPubKey, vr)
}
}
// Add is a simple way to add imports
func (i *Imports) Add(a ...*Import) {
*i = append(*i, a...)
}
func (i Imports) Len() int {
return len(i)
}
func (i Imports) Swap(j, k int) {
i[j], i[k] = i[k], i[j]
}
func (i Imports) Less(j, k int) bool {
return i[j].Subject < i[k].Subject
}

204
vendor/github.com/nats-io/jwt/operator_claims.go generated vendored Normal file
View File

@ -0,0 +1,204 @@
/*
* Copyright 2018 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"errors"
"fmt"
"net/url"
"strings"
"github.com/nats-io/nkeys"
)
// Operator specific claims
type Operator struct {
// Slice of real identies (like websites) that can be used to identify the operator.
Identities []Identity `json:"identity,omitempty"`
// Slice of other operator NKeys that can be used to sign on behalf of the main
// operator identity.
SigningKeys StringList `json:"signing_keys,omitempty"`
// AccountServerURL is a partial URL like "https://host.domain.org:<port>/jwt/v1"
// tools will use the prefix and build queries by appending /accounts/<account_id>
// or /operator to the path provided. Note this assumes that the account server
// can handle requests in a nats-account-server compatible way. See
// https://github.com/nats-io/nats-account-server.
AccountServerURL string `json:"account_server_url,omitempty"`
// A list of NATS urls (tls://host:port) where tools can connect to the server
// using proper credentials.
OperatorServiceURLs StringList `json:"operator_service_urls,omitempty"`
}
// Validate checks the validity of the operators contents
func (o *Operator) Validate(vr *ValidationResults) {
if err := o.validateAccountServerURL(); err != nil {
vr.AddError(err.Error())
}
for _, v := range o.validateOperatorServiceURLs() {
if v != nil {
vr.AddError(v.Error())
}
}
for _, i := range o.Identities {
i.Validate(vr)
}
for _, k := range o.SigningKeys {
if !nkeys.IsValidPublicOperatorKey(k) {
vr.AddError("%s is not an operator public key", k)
}
}
}
func (o *Operator) validateAccountServerURL() error {
if o.AccountServerURL != "" {
// We don't care what kind of URL it is so long as it parses
// and has a protocol. The account server may impose additional
// constraints on the type of URLs that it is able to notify to
u, err := url.Parse(o.AccountServerURL)
if err != nil {
return fmt.Errorf("error parsing account server url: %v", err)
}
if u.Scheme == "" {
return fmt.Errorf("account server url %q requires a protocol", o.AccountServerURL)
}
}
return nil
}
// ValidateOperatorServiceURL returns an error if the URL is not a valid NATS or TLS url.
func ValidateOperatorServiceURL(v string) error {
// should be possible for the service url to not be expressed
if v == "" {
return nil
}
u, err := url.Parse(v)
if err != nil {
return fmt.Errorf("error parsing operator service url %q: %v", v, err)
}
if u.User != nil {
return fmt.Errorf("operator service url %q - credentials are not supported", v)
}
if u.Path != "" {
return fmt.Errorf("operator service url %q - paths are not supported", v)
}
lcs := strings.ToLower(u.Scheme)
switch lcs {
case "nats":
return nil
case "tls":
return nil
default:
return fmt.Errorf("operator service url %q - protocol not supported (only 'nats' or 'tls' only)", v)
}
}
func (o *Operator) validateOperatorServiceURLs() []error {
var errors []error
for _, v := range o.OperatorServiceURLs {
if v != "" {
if err := ValidateOperatorServiceURL(v); err != nil {
errors = append(errors, err)
}
}
}
return errors
}
// OperatorClaims define the data for an operator JWT
type OperatorClaims struct {
ClaimsData
Operator `json:"nats,omitempty"`
}
// NewOperatorClaims creates a new operator claim with the specified subject, which should be an operator public key
func NewOperatorClaims(subject string) *OperatorClaims {
if subject == "" {
return nil
}
c := &OperatorClaims{}
c.Subject = subject
return c
}
// DidSign checks the claims against the operator's public key and its signing keys
func (oc *OperatorClaims) DidSign(op Claims) bool {
if op == nil {
return false
}
issuer := op.Claims().Issuer
if issuer == oc.Subject {
return true
}
return oc.SigningKeys.Contains(issuer)
}
// Deprecated: AddSigningKey, use claim.SigningKeys.Add()
func (oc *OperatorClaims) AddSigningKey(pk string) {
oc.SigningKeys.Add(pk)
}
// Encode the claims into a JWT string
func (oc *OperatorClaims) Encode(pair nkeys.KeyPair) (string, error) {
if !nkeys.IsValidPublicOperatorKey(oc.Subject) {
return "", errors.New("expected subject to be an operator public key")
}
err := oc.validateAccountServerURL()
if err != nil {
return "", err
}
oc.ClaimsData.Type = OperatorClaim
return oc.ClaimsData.Encode(pair, oc)
}
// DecodeOperatorClaims tries to create an operator claims from a JWt string
func DecodeOperatorClaims(token string) (*OperatorClaims, error) {
v := OperatorClaims{}
if err := Decode(token, &v); err != nil {
return nil, err
}
return &v, nil
}
func (oc *OperatorClaims) String() string {
return oc.ClaimsData.String(oc)
}
// Payload returns the operator specific data for an operator JWT
func (oc *OperatorClaims) Payload() interface{} {
return &oc.Operator
}
// Validate the contents of the claims
func (oc *OperatorClaims) Validate(vr *ValidationResults) {
oc.ClaimsData.Validate(vr)
oc.Operator.Validate(vr)
}
// ExpectedPrefixes defines the nkey types that can sign operator claims, operator
func (oc *OperatorClaims) ExpectedPrefixes() []nkeys.PrefixByte {
return []nkeys.PrefixByte{nkeys.PrefixByteOperator}
}
// Claims returns the generic claims data
func (oc *OperatorClaims) Claims() *ClaimsData {
return &oc.ClaimsData
}

32
vendor/github.com/nats-io/jwt/revocation_list.go generated vendored Normal file
View File

@ -0,0 +1,32 @@
package jwt
import (
"time"
)
// RevocationList is used to store a mapping of public keys to unix timestamps
type RevocationList map[string]int64
// Revoke enters a revocation by publickey and timestamp into this export
// If there is already a revocation for this public key that is newer, it is kept.
func (r RevocationList) Revoke(pubKey string, timestamp time.Time) {
newTS := timestamp.Unix()
if ts, ok := r[pubKey]; ok && ts > newTS {
return
}
r[pubKey] = newTS
}
// ClearRevocation removes any revocation for the public key
func (r RevocationList) ClearRevocation(pubKey string) {
delete(r, pubKey)
}
// IsRevoked checks if the public key is in the revoked list with a timestamp later than
// the one passed in. Generally this method is called with time.Now() but other time's can
// be used for testing.
func (r RevocationList) IsRevoked(pubKey string, timestamp time.Time) bool {
ts, ok := r[pubKey]
return ok && ts > timestamp.Unix()
}

94
vendor/github.com/nats-io/jwt/server_claims.go generated vendored Normal file
View File

@ -0,0 +1,94 @@
/*
* Copyright 2018 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"errors"
"github.com/nats-io/nkeys"
)
// Server defines the custom part of a server jwt
type Server struct {
Permissions
Cluster string `json:"cluster,omitempty"`
}
// Validate checks the cluster and permissions for a server JWT
func (s *Server) Validate(vr *ValidationResults) {
if s.Cluster == "" {
vr.AddError("servers can't contain an empty cluster")
}
}
// ServerClaims defines the data in a server JWT
type ServerClaims struct {
ClaimsData
Server `json:"nats,omitempty"`
}
// NewServerClaims creates a new server JWT with the specified subject/public key
func NewServerClaims(subject string) *ServerClaims {
if subject == "" {
return nil
}
c := &ServerClaims{}
c.Subject = subject
return c
}
// Encode tries to turn the server claims into a JWT string
func (s *ServerClaims) Encode(pair nkeys.KeyPair) (string, error) {
if !nkeys.IsValidPublicServerKey(s.Subject) {
return "", errors.New("expected subject to be a server public key")
}
s.ClaimsData.Type = ServerClaim
return s.ClaimsData.Encode(pair, s)
}
// DecodeServerClaims tries to parse server claims from a JWT string
func DecodeServerClaims(token string) (*ServerClaims, error) {
v := ServerClaims{}
if err := Decode(token, &v); err != nil {
return nil, err
}
return &v, nil
}
func (s *ServerClaims) String() string {
return s.ClaimsData.String(s)
}
// Payload returns the server specific data
func (s *ServerClaims) Payload() interface{} {
return &s.Server
}
// Validate checks the generic and server data in the server claims
func (s *ServerClaims) Validate(vr *ValidationResults) {
s.ClaimsData.Validate(vr)
s.Server.Validate(vr)
}
// ExpectedPrefixes defines the types that can encode a server JWT, operator or cluster
func (s *ServerClaims) ExpectedPrefixes() []nkeys.PrefixByte {
return []nkeys.PrefixByte{nkeys.PrefixByteOperator, nkeys.PrefixByteCluster}
}
// Claims returns the generic data
func (s *ServerClaims) Claims() *ClaimsData {
return &s.ClaimsData
}

334
vendor/github.com/nats-io/jwt/types.go generated vendored Normal file
View File

@ -0,0 +1,334 @@
/*
* Copyright 2018-2019 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"encoding/json"
"fmt"
"net"
"strings"
"time"
)
// ExportType defines the type of import/export.
type ExportType int
const (
// Unknown is used if we don't know the type
Unknown ExportType = iota
// Stream defines the type field value for a stream "stream"
Stream
// Service defines the type field value for a service "service"
Service
)
func (t ExportType) String() string {
switch t {
case Stream:
return "stream"
case Service:
return "service"
}
return "unknown"
}
// MarshalJSON marshals the enum as a quoted json string
func (t *ExportType) MarshalJSON() ([]byte, error) {
switch *t {
case Stream:
return []byte("\"stream\""), nil
case Service:
return []byte("\"service\""), nil
}
return nil, fmt.Errorf("unknown export type")
}
// UnmarshalJSON unmashals a quoted json string to the enum value
func (t *ExportType) UnmarshalJSON(b []byte) error {
var j string
err := json.Unmarshal(b, &j)
if err != nil {
return err
}
switch j {
case "stream":
*t = Stream
return nil
case "service":
*t = Service
return nil
}
return fmt.Errorf("unknown export type")
}
// Subject is a string that represents a NATS subject
type Subject string
// Validate checks that a subject string is valid, ie not empty and without spaces
func (s Subject) Validate(vr *ValidationResults) {
v := string(s)
if v == "" {
vr.AddError("subject cannot be empty")
}
if strings.Contains(v, " ") {
vr.AddError("subject %q cannot have spaces", v)
}
}
// HasWildCards is used to check if a subject contains a > or *
func (s Subject) HasWildCards() bool {
v := string(s)
return strings.HasSuffix(v, ".>") ||
strings.Contains(v, ".*.") ||
strings.HasSuffix(v, ".*") ||
strings.HasPrefix(v, "*.") ||
v == "*" ||
v == ">"
}
// IsContainedIn does a simple test to see if the subject is contained in another subject
func (s Subject) IsContainedIn(other Subject) bool {
otherArray := strings.Split(string(other), ".")
myArray := strings.Split(string(s), ".")
if len(myArray) > len(otherArray) && otherArray[len(otherArray)-1] != ">" {
return false
}
if len(myArray) < len(otherArray) {
return false
}
for ind, tok := range otherArray {
myTok := myArray[ind]
if ind == len(otherArray)-1 && tok == ">" {
return true
}
if tok != myTok && tok != "*" {
return false
}
}
return true
}
// NamedSubject is the combination of a subject and a name for it
type NamedSubject struct {
Name string `json:"name,omitempty"`
Subject Subject `json:"subject,omitempty"`
}
// Validate checks the subject
func (ns *NamedSubject) Validate(vr *ValidationResults) {
ns.Subject.Validate(vr)
}
// TimeRange is used to represent a start and end time
type TimeRange struct {
Start string `json:"start,omitempty"`
End string `json:"end,omitempty"`
}
// Validate checks the values in a time range struct
func (tr *TimeRange) Validate(vr *ValidationResults) {
format := "15:04:05"
if tr.Start == "" {
vr.AddError("time ranges start must contain a start")
} else {
_, err := time.Parse(format, tr.Start)
if err != nil {
vr.AddError("start in time range is invalid %q", tr.Start)
}
}
if tr.End == "" {
vr.AddError("time ranges end must contain an end")
} else {
_, err := time.Parse(format, tr.End)
if err != nil {
vr.AddError("end in time range is invalid %q", tr.End)
}
}
}
// Limits are used to control acccess for users and importing accounts
// Src is a comma separated list of CIDR specifications
type Limits struct {
Max int64 `json:"max,omitempty"`
Payload int64 `json:"payload,omitempty"`
Src string `json:"src,omitempty"`
Times []TimeRange `json:"times,omitempty"`
}
// Validate checks the values in a limit struct
func (l *Limits) Validate(vr *ValidationResults) {
if l.Max < 0 {
vr.AddError("limits cannot contain a negative maximum, %d", l.Max)
}
if l.Payload < 0 {
vr.AddError("limits cannot contain a negative payload, %d", l.Payload)
}
if l.Src != "" {
elements := strings.Split(l.Src, ",")
for _, cidr := range elements {
cidr = strings.TrimSpace(cidr)
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil || ipNet == nil {
vr.AddError("invalid cidr %q in user src limits", cidr)
}
}
}
if l.Times != nil && len(l.Times) > 0 {
for _, t := range l.Times {
t.Validate(vr)
}
}
}
// Permission defines allow/deny subjects
type Permission struct {
Allow StringList `json:"allow,omitempty"`
Deny StringList `json:"deny,omitempty"`
}
// Validate the allow, deny elements of a permission
func (p *Permission) Validate(vr *ValidationResults) {
for _, subj := range p.Allow {
Subject(subj).Validate(vr)
}
for _, subj := range p.Deny {
Subject(subj).Validate(vr)
}
}
// ResponsePermission can be used to allow responses to any reply subject
// that is received on a valid subscription.
type ResponsePermission struct {
MaxMsgs int `json:"max"`
Expires time.Duration `json:"ttl"`
}
// Validate the response permission.
func (p *ResponsePermission) Validate(vr *ValidationResults) {
// Any values can be valid for now.
}
// Permissions are used to restrict subject access, either on a user or for everyone on a server by default
type Permissions struct {
Pub Permission `json:"pub,omitempty"`
Sub Permission `json:"sub,omitempty"`
Resp *ResponsePermission `json:"resp,omitempty"`
}
// Validate the pub and sub fields in the permissions list
func (p *Permissions) Validate(vr *ValidationResults) {
p.Pub.Validate(vr)
p.Sub.Validate(vr)
if p.Resp != nil {
p.Resp.Validate(vr)
}
}
// StringList is a wrapper for an array of strings
type StringList []string
// Contains returns true if the list contains the string
func (u *StringList) Contains(p string) bool {
for _, t := range *u {
if t == p {
return true
}
}
return false
}
// Add appends 1 or more strings to a list
func (u *StringList) Add(p ...string) {
for _, v := range p {
if !u.Contains(v) && v != "" {
*u = append(*u, v)
}
}
}
// Remove removes 1 or more strings from a list
func (u *StringList) Remove(p ...string) {
for _, v := range p {
for i, t := range *u {
if t == v {
a := *u
*u = append(a[:i], a[i+1:]...)
break
}
}
}
}
// TagList is a unique array of lower case strings
// All tag list methods lower case the strings in the arguments
type TagList []string
// Contains returns true if the list contains the tags
func (u *TagList) Contains(p string) bool {
p = strings.ToLower(p)
for _, t := range *u {
if t == p {
return true
}
}
return false
}
// Add appends 1 or more tags to a list
func (u *TagList) Add(p ...string) {
for _, v := range p {
v = strings.ToLower(v)
if !u.Contains(v) && v != "" {
*u = append(*u, v)
}
}
}
// Remove removes 1 or more tags from a list
func (u *TagList) Remove(p ...string) {
for _, v := range p {
v = strings.ToLower(v)
for i, t := range *u {
if t == v {
a := *u
*u = append(a[:i], a[i+1:]...)
break
}
}
}
}
// Identity is used to associate an account or operator with a real entity
type Identity struct {
ID string `json:"id,omitempty"`
Proof string `json:"proof,omitempty"`
}
// Validate checks the values in an Identity
func (u *Identity) Validate(vr *ValidationResults) {
//Fixme identity validation
}

99
vendor/github.com/nats-io/jwt/user_claims.go generated vendored Normal file
View File

@ -0,0 +1,99 @@
/*
* Copyright 2018-2019 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"errors"
"github.com/nats-io/nkeys"
)
// User defines the user specific data in a user JWT
type User struct {
Permissions
Limits
}
// Validate checks the permissions and limits in a User jwt
func (u *User) Validate(vr *ValidationResults) {
u.Permissions.Validate(vr)
u.Limits.Validate(vr)
}
// UserClaims defines a user JWT
type UserClaims struct {
ClaimsData
User `json:"nats,omitempty"`
// IssuerAccount stores the public key for the account the issuer represents.
// When set, the claim was issued by a signing key.
IssuerAccount string `json:"issuer_account,omitempty"`
}
// NewUserClaims creates a user JWT with the specific subject/public key
func NewUserClaims(subject string) *UserClaims {
if subject == "" {
return nil
}
c := &UserClaims{}
c.Subject = subject
return c
}
// Encode tries to turn the user claims into a JWT string
func (u *UserClaims) Encode(pair nkeys.KeyPair) (string, error) {
if !nkeys.IsValidPublicUserKey(u.Subject) {
return "", errors.New("expected subject to be user public key")
}
u.ClaimsData.Type = UserClaim
return u.ClaimsData.Encode(pair, u)
}
// DecodeUserClaims tries to parse a user claims from a JWT string
func DecodeUserClaims(token string) (*UserClaims, error) {
v := UserClaims{}
if err := Decode(token, &v); err != nil {
return nil, err
}
return &v, nil
}
// Validate checks the generic and specific parts of the user jwt
func (u *UserClaims) Validate(vr *ValidationResults) {
u.ClaimsData.Validate(vr)
u.User.Validate(vr)
if u.IssuerAccount != "" && !nkeys.IsValidPublicAccountKey(u.IssuerAccount) {
vr.AddError("account_id is not an account public key")
}
}
// ExpectedPrefixes defines the types that can encode a user JWT, account
func (u *UserClaims) ExpectedPrefixes() []nkeys.PrefixByte {
return []nkeys.PrefixByte{nkeys.PrefixByteAccount}
}
// Claims returns the generic data from a user jwt
func (u *UserClaims) Claims() *ClaimsData {
return &u.ClaimsData
}
// Payload returns the user specific data from a user JWT
func (u *UserClaims) Payload() interface{} {
return &u.User
}
func (u *UserClaims) String() string {
return u.ClaimsData.String(u)
}

107
vendor/github.com/nats-io/jwt/validation.go generated vendored Normal file
View File

@ -0,0 +1,107 @@
/*
* Copyright 2018 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"errors"
"fmt"
)
// ValidationIssue represents an issue during JWT validation, it may or may not be a blocking error
type ValidationIssue struct {
Description string
Blocking bool
TimeCheck bool
}
func (ve *ValidationIssue) Error() string {
return ve.Description
}
// ValidationResults is a list of ValidationIssue pointers
type ValidationResults struct {
Issues []*ValidationIssue
}
// CreateValidationResults creates an empty list of validation issues
func CreateValidationResults() *ValidationResults {
issues := []*ValidationIssue{}
return &ValidationResults{
Issues: issues,
}
}
//Add appends an issue to the list
func (v *ValidationResults) Add(vi *ValidationIssue) {
v.Issues = append(v.Issues, vi)
}
// AddError creates a new validation error and adds it to the list
func (v *ValidationResults) AddError(format string, args ...interface{}) {
v.Add(&ValidationIssue{
Description: fmt.Sprintf(format, args...),
Blocking: true,
TimeCheck: false,
})
}
// AddTimeCheck creates a new validation issue related to a time check and adds it to the list
func (v *ValidationResults) AddTimeCheck(format string, args ...interface{}) {
v.Add(&ValidationIssue{
Description: fmt.Sprintf(format, args...),
Blocking: false,
TimeCheck: true,
})
}
// AddWarning creates a new validation warning and adds it to the list
func (v *ValidationResults) AddWarning(format string, args ...interface{}) {
v.Add(&ValidationIssue{
Description: fmt.Sprintf(format, args...),
Blocking: false,
TimeCheck: false,
})
}
// IsBlocking returns true if the list contains a blocking error
func (v *ValidationResults) IsBlocking(includeTimeChecks bool) bool {
for _, i := range v.Issues {
if i.Blocking {
return true
}
if includeTimeChecks && i.TimeCheck {
return true
}
}
return false
}
// IsEmpty returns true if the list is empty
func (v *ValidationResults) IsEmpty() bool {
return len(v.Issues) == 0
}
// Errors returns only blocking issues as errors
func (v *ValidationResults) Errors() []error {
var errs []error
for _, v := range v.Issues {
if v.Blocking {
errs = append(errs, errors.New(v.Description))
}
}
return errs
}

View File

@ -37,3 +37,6 @@ _testmain.go
.settings/
# bin
# Goland
.idea

23
vendor/github.com/nats-io/nats.go/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,23 @@
language: go
sudo: false
go:
- 1.12.x
- 1.11.x
env:
- GO111MODULE=off
go_import_path: github.com/nats-io/nats.go
install:
- go get -t ./...
- go get github.com/nats-io/nats-server
- go get github.com/mattn/goveralls
- go get github.com/wadey/gocovmerge
- go get -u honnef.co/go/tools/cmd/staticcheck
- go get -u github.com/client9/misspell/cmd/misspell
before_script:
- $(exit $(go fmt ./... | wc -l))
- go vet ./...
- find . -type f -name "*.go" | xargs misspell -error -locale US
- staticcheck ./...
script:
- go test -i -race ./...
- if [[ "$TRAVIS_GO_VERSION" =~ 1.12 ]]; then ./scripts/cov.sh TRAVIS; else go test -race -v -p=1 ./... --failfast; fi

201
vendor/github.com/nats-io/nats.go/LICENSE generated vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -3,21 +3,36 @@ A [Go](http://golang.org) client for the [NATS messaging system](https://nats.io
[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fnats-io%2Fgo-nats.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fnats-io%2Fgo-nats?ref=badge_shield)
[![Go Report Card](https://goreportcard.com/badge/github.com/nats-io/go-nats)](https://goreportcard.com/report/github.com/nats-io/go-nats) [![Build Status](https://travis-ci.org/nats-io/go-nats.svg?branch=master)](http://travis-ci.org/nats-io/go-nats) [![GoDoc](https://godoc.org/github.com/nats-io/go-nats?status.svg)](http://godoc.org/github.com/nats-io/go-nats) [![Coverage Status](https://coveralls.io/repos/nats-io/go-nats/badge.svg?branch=master)](https://coveralls.io/r/nats-io/go-nats?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/nats-io/nats.go)](https://goreportcard.com/report/github.com/nats-io/nats.go) [![Build Status](https://travis-ci.org/nats-io/nats.go.svg?branch=master)](http://travis-ci.org/nats-io/nats.go) [![GoDoc](https://godoc.org/github.com/nats-io/nats.go?status.svg)](http://godoc.org/github.com/nats-io/nats.go) [![Coverage Status](https://coveralls.io/repos/nats-io/nats.go/badge.svg?branch=master)](https://coveralls.io/r/nats-io/nats.go?branch=master)
## Installation
```bash
# Go client
go get github.com/nats-io/go-nats
go get github.com/nats-io/nats.go/
# Server
go get github.com/nats-io/gnatsd
go get github.com/nats-io/nats-server
```
When using or transitioning to Go modules support:
```bash
# Go client latest or explicit version
go get github.com/nats-io/nats.go/@latest
go get github.com/nats-io/nats.go/@v1.9.1
# For latest NATS Server, add /v2 at the end
go get github.com/nats-io/nats-server/v2
# NATS Server v1 is installed otherwise
# go get github.com/nats-io/nats-server
```
## Basic Usage
```go
import nats "github.com/nats-io/nats.go"
// Connect to a server
nc, _ := nats.Connect(nats.DefaultURL)
@ -30,6 +45,11 @@ nc.Subscribe("foo", func(m *nats.Msg) {
fmt.Printf("Received a message: %s\n", string(m.Data))
})
// Responding to a request message
nc.Subscribe("request", func(m *nats.Msg) {
m.Respond([]byte("answer is 42"))
})
// Simple Sync Subscriber
sub, err := nc.SubscribeSync("foo")
m, err := sub.NextMsg(timeout)
@ -49,7 +69,7 @@ sub.Drain()
msg, err := nc.Request("help", []byte("help me"), 10*time.Millisecond)
// Replies
nc.Subscribe("help", func(m *Msg) {
nc.Subscribe("help", func(m *nats.Msg) {
nc.Publish(m.Reply, []byte("I can help!"))
})
@ -96,12 +116,12 @@ c.Publish("hello", me)
// Unsubscribe
sub, err := c.Subscribe("foo", nil)
...
// ...
sub.Unsubscribe()
// Requests
var response string
err := c.Request("help", "help me", &response, 10*time.Millisecond)
err = c.Request("help", "help me", &response, 10*time.Millisecond)
if err != nil {
fmt.Printf("Request failed: %v\n", err)
}
@ -115,6 +135,48 @@ c.Subscribe("help", func(subj, reply string, msg string) {
c.Close();
```
## New Authentication (Nkeys and User Credentials)
This requires server with version >= 2.0.0
NATS servers have a new security and authentication mechanism to authenticate with user credentials and Nkeys.
The simplest form is to use the helper method UserCredentials(credsFilepath).
```go
nc, err := nats.Connect(url, nats.UserCredentials("user.creds"))
```
The helper methods creates two callback handlers to present the user JWT and sign the nonce challenge from the server.
The core client library never has direct access to your private key and simply performs the callback for signing the server challenge.
The helper will load and wipe and erase memory it uses for each connect or reconnect.
The helper also can take two entries, one for the JWT and one for the NKey seed file.
```go
nc, err := nats.Connect(url, nats.UserCredentials("user.jwt", "user.nk"))
```
You can also set the callback handlers directly and manage challenge signing directly.
```go
nc, err := nats.Connect(url, nats.UserJWT(jwtCB, sigCB))
```
Bare Nkeys are also supported. The nkey seed should be in a read only file, e.g. seed.txt
```bash
> cat seed.txt
# This is my seed nkey!
SUAGMJH5XLGZKQQWAWKRZJIGMOU4HPFUYLXJMXOO5NLFEO2OOQJ5LPRDPM
```
This is a helper function which will load and decode and do the proper signing for the server nonce.
It will clear memory in between invocations.
You can choose to use the low level option and provide the public key and a signature callback on your own.
```go
opt, err := nats.NkeyOptionFromSeed("seed.txt")
nc, err := nats.Connect(serverUrl, opt)
// Direct
nc, err := nats.Connect(serverUrl, nats.Nkey(pubNkey, sigCB))
```
## TLS
```go
@ -268,10 +330,10 @@ nc, err = nats.Connect(servers, nats.DontRandomize())
// Setup callbacks to be notified on disconnects, reconnects and connection closed.
nc, err = nats.Connect(servers,
nats.DisconnectHandler(func(nc *nats.Conn) {
fmt.Printf("Got disconnected!\n")
nats.DisconnectErrHandler(func(nc *nats.Conn, err error) {
fmt.Printf("Got disconnected! Reason: %q\n", err)
}),
nats.ReconnectHandler(func(_ *nats.Conn) {
nats.ReconnectHandler(func(nc *nats.Conn) {
fmt.Printf("Got reconnected to %v!\n", nc.ConnectedUrl())
}),
nats.ClosedHandler(func(nc *nats.Conn) {
@ -296,9 +358,9 @@ nc, err = nats.Connect("nats://localhost:4222",
nats.Token("S3cretT0ken"))
// Note that if credentials are specified in the initial URLs, they take
// precedence on the credentials specfied through the options.
// precedence on the credentials specified through the options.
// For instance, in the connect call below, the client library will use
// the user "my" and password "pwd" to connect to locahost:4222, however,
// the user "my" and password "pwd" to connect to localhost:4222, however,
// it will use username "foo" and password "bar" when (re)connecting to
// a different server URL that it got as part of the auto-discovery.
nc, err = nats.Connect("nats://my:pwd@localhost:4222", nats.UserInfo("foo", "bar"))

View File

@ -18,7 +18,6 @@ package nats
import (
"context"
"fmt"
"reflect"
)
@ -44,31 +43,7 @@ func (nc *Conn) RequestWithContext(ctx context.Context, subj string, data []byte
return nc.oldRequestWithContext(ctx, subj, data)
}
// Do setup for the new style.
if nc.respMap == nil {
// _INBOX wildcard
nc.respSub = fmt.Sprintf("%s.*", NewInbox())
nc.respMap = make(map[string]chan *Msg)
}
// Create literal Inbox and map to a chan msg.
mch := make(chan *Msg, RequestChanLen)
respInbox := nc.newRespInbox()
token := respToken(respInbox)
nc.respMap[token] = mch
createSub := nc.respMux == nil
ginbox := nc.respSub
nc.mu.Unlock()
if createSub {
// Make sure scoped subscription is setup only once.
var err error
nc.respSetup.Do(func() { err = nc.createRespMux(ginbox) })
if err != nil {
return nil, err
}
}
err := nc.PublishRequest(subj, respInbox, data)
mch, token, err := nc.createNewRequestAndSend(subj, data)
if err != nil {
return nil, err
}
@ -96,7 +71,7 @@ func (nc *Conn) oldRequestWithContext(ctx context.Context, subj string, data []b
inbox := NewInbox()
ch := make(chan *Msg, RequestChanLen)
s, err := nc.subscribe(inbox, _EMPTY_, nil, ch)
s, err := nc.subscribe(inbox, _EMPTY_, nil, ch, true)
if err != nil {
return nil, err
}
@ -132,19 +107,33 @@ func (s *Subscription) NextMsgWithContext(ctx context.Context) (*Msg, error) {
return nil, err
}
// snapshot
mch := s.mch
s.mu.Unlock()
var ok bool
var msg *Msg
// If something is available right away, let's optimize that case.
select {
case msg, ok = <-mch:
if !ok {
return nil, ErrConnectionClosed
return nil, s.getNextMsgErr()
}
err := s.processNextMsgDelivered(msg)
if err != nil {
if err := s.processNextMsgDelivered(msg); err != nil {
return nil, err
} else {
return msg, nil
}
default:
}
select {
case msg, ok = <-mch:
if !ok {
return nil, s.getNextMsgErr()
}
if err := s.processNextMsgDelivered(msg); err != nil {
return nil, err
}
case <-ctx.Done():
@ -154,6 +143,52 @@ func (s *Subscription) NextMsgWithContext(ctx context.Context) (*Msg, error) {
return msg, nil
}
// FlushWithContext will allow a context to control the duration
// of a Flush() call. This context should be non-nil and should
// have a deadline set. We will return an error if none is present.
func (nc *Conn) FlushWithContext(ctx context.Context) error {
if nc == nil {
return ErrInvalidConnection
}
if ctx == nil {
return ErrInvalidContext
}
_, ok := ctx.Deadline()
if !ok {
return ErrNoDeadlineContext
}
nc.mu.Lock()
if nc.isClosed() {
nc.mu.Unlock()
return ErrConnectionClosed
}
// Create a buffered channel to prevent chan send to block
// in processPong()
ch := make(chan struct{}, 1)
nc.sendPing(ch)
nc.mu.Unlock()
var err error
select {
case _, ok := <-ch:
if !ok {
err = ErrConnectionClosed
} else {
close(ch)
}
case <-ctx.Done():
err = ctx.Err()
}
if err != nil {
nc.removeFlushEntry(ch)
}
return err
}
// RequestWithContext will create an Inbox and perform a Request
// using the provided cancellation context with the Inbox reply
// for the data v. A response will be decoded into the vPtrResponse.

Some files were not shown because too many files have changed in this diff Show More