From e778278159d3fc5bb364705bcef0e49ccf2a5a2c Mon Sep 17 00:00:00 2001 From: Mirko Teodorovic Date: Tue, 15 Dec 2020 11:35:53 +0100 Subject: [PATCH 01/39] remove owner id Signed-off-by: Mirko Teodorovic --- users/postgres/init.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/users/postgres/init.go b/users/postgres/init.go index 821c802ac7..34a15aabdf 100644 --- a/users/postgres/init.go +++ b/users/postgres/init.go @@ -78,6 +78,13 @@ func migrateDB(db *sqlx.DB) error { `ALTER TABLE IF EXISTS users ADD PRIMARY KEY (id)`, }, }, + { + Id: "users_5", + Up: []string{ + `ALTER TABLE IF EXISTS users DROP CONSTRAINT IF EXISTS users_owner_id_fkey`, + `ALTER TABLE IF EXISTS users DROP COLUMN IF EXISTS owner_id`, + }, + }, }, } From 51fc5e152e15e4be0abe3246fa9297e39fb22322 Mon Sep 17 00:00:00 2001 From: mteodor Date: Mon, 14 Jun 2021 11:15:04 +0200 Subject: [PATCH 02/39] add user auth for db reader Signed-off-by: mteodor --- cmd/influxdb-reader/main.go | 58 +++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/cmd/influxdb-reader/main.go b/cmd/influxdb-reader/main.go index e3574eb4e5..809f27a5e7 100644 --- a/cmd/influxdb-reader/main.go +++ b/cmd/influxdb-reader/main.go @@ -15,11 +15,13 @@ import ( kitprometheus "github.com/go-kit/kit/metrics/prometheus" influxdata "github.com/influxdata/influxdb/client/v2" "github.com/mainflux/mainflux" + authapi "github.com/mainflux/mainflux/auth/api/grpc" "github.com/mainflux/mainflux/logger" "github.com/mainflux/mainflux/readers" "github.com/mainflux/mainflux/readers/api" "github.com/mainflux/mainflux/readers/influxdb" thingsapi "github.com/mainflux/mainflux/things/api/auth/grpc" + opentracing "github.com/opentracing/opentracing-go" stdprometheus "github.com/prometheus/client_golang/prometheus" jconfig "github.com/uber/jaeger-client-go/config" @@ -40,8 +42,10 @@ const ( defServerCert = "" defServerKey = "" defJaegerURL = "" - defThingsAuthURL = "localhost:8181" + defThingsAuthURL = "localhost:8183" + defUsersAuthURL = "localhost:8181" defThingsAuthTimeout = "1s" + defUsersAuthTimeout = "1s" envLogLevel = "MF_INFLUX_READER_LOG_LEVEL" envPort = "MF_INFLUX_READER_PORT" @@ -57,6 +61,8 @@ const ( envJaegerURL = "MF_JAEGER_URL" envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL" envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT" + envAuthURL = "MF_AUTH_GRPC_URL" + envUsersAuthTimeout = "MF_AUTH_GRPC_TIMEOUT" ) type config struct { @@ -73,7 +79,9 @@ type config struct { serverKey string jaegerURL string thingsAuthURL string + usersAuthURL string thingsAuthTimeout time.Duration + usersAuthTimeout time.Duration } func main() { @@ -90,6 +98,16 @@ func main() { tc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsAuthTimeout) + authTracer, authCloser := initJaeger("auth", cfg.jaegerURL, logger) + defer authCloser.Close() + + authConn := connectToAuth(cfg, logger) + defer authConn.Close() + + auth := authapi.NewClient(authTracer, authConn, cfg.usersAuthTimeout) + + authReader := readers.NewAuthService(tc, auth) + client, err := influxdata.NewHTTPClient(clientCfg) if err != nil { logger.Error(fmt.Sprintf("Failed to create InfluxDB client: %s", err)) @@ -106,12 +124,38 @@ func main() { errs <- fmt.Errorf("%s", <-c) }() - go startHTTPServer(repo, tc, cfg, logger, errs) + go startHTTPServer(repo, authReader, cfg, logger, errs) err = <-errs logger.Error(fmt.Sprintf("InfluxDB writer service terminated: %s", err)) } +func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { + var opts []grpc.DialOption + logger.Info("connecting to auth via gRPC") + if cfg.clientTLS { + if cfg.caCerts != "" { + tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "") + if err != nil { + logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err)) + os.Exit(1) + } + opts = append(opts, grpc.WithTransportCredentials(tpc)) + } + } else { + opts = append(opts, grpc.WithInsecure()) + logger.Info("gRPC communication is not encrypted") + } + + conn, err := grpc.Dial(cfg.usersAuthURL, opts...) + if err != nil { + logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err)) + os.Exit(1) + } + + return conn +} + func loadConfigs() (config, influxdata.HTTPConfig) { tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS)) if err != nil { @@ -123,6 +167,11 @@ func loadConfigs() (config, influxdata.HTTPConfig) { log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error()) } + userAuthTimeout, err := time.ParseDuration(mainflux.Env(envUsersAuthTimeout, defUsersAuthTimeout)) + if err != nil { + log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error()) + } + cfg := config{ logLevel: mainflux.Env(envLogLevel, defLogLevel), port: mainflux.Env(envPort, defPort), @@ -138,6 +187,8 @@ func loadConfigs() (config, influxdata.HTTPConfig) { jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL), thingsAuthURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL), thingsAuthTimeout: authTimeout, + usersAuthURL: mainflux.Env(envAuthURL, defUsersAuthURL), + usersAuthTimeout: userAuthTimeout, } clientCfg := influxdata.HTTPConfig{ @@ -151,6 +202,7 @@ func loadConfigs() (config, influxdata.HTTPConfig) { func connectToThings(cfg config, logger logger.Logger) *grpc.ClientConn { var opts []grpc.DialOption + logger.Info("connecting to things via gRPC") if cfg.clientTLS { if cfg.caCerts != "" { tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "") @@ -219,7 +271,7 @@ func newService(client influxdata.Client, dbName string, logger logger.Logger) r return repo } -func startHTTPServer(repo readers.MessageRepository, tc mainflux.ThingsServiceClient, cfg config, logger logger.Logger, errs chan error) { +func startHTTPServer(repo readers.MessageRepository, tc readers.Auth, cfg config, logger logger.Logger, errs chan error) { p := fmt.Sprintf(":%s", cfg.port) if cfg.serverCert != "" || cfg.serverKey != "" { logger.Info(fmt.Sprintf("InfluxDB reader service started using https on port %s with cert %s key %s", From 0809e6f46a85614da39f8b41c63845c6d2a79be6 Mon Sep 17 00:00:00 2001 From: mteodor Date: Mon, 14 Jun 2021 11:15:18 +0200 Subject: [PATCH 03/39] add user auth for db reader Signed-off-by: mteodor --- readers/api/endpoint_test.go | 8 ++++--- readers/api/transport.go | 36 +++++++++++++++++++++++++---- readers/auth.go | 44 ++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 readers/auth.go diff --git a/readers/api/endpoint_test.go b/readers/api/endpoint_test.go index cbe712dcc3..2f6f0494e3 100644 --- a/readers/api/endpoint_test.go +++ b/readers/api/endpoint_test.go @@ -12,12 +12,12 @@ import ( "testing" "time" - "github.com/mainflux/mainflux" "github.com/mainflux/mainflux/pkg/transformers/senml" "github.com/mainflux/mainflux/pkg/uuid" "github.com/mainflux/mainflux/readers" "github.com/mainflux/mainflux/readers/api" "github.com/mainflux/mainflux/readers/mocks" + authmocks "github.com/mainflux/mainflux/users/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -44,7 +44,7 @@ var ( idProvider = uuid.New() ) -func newServer(repo readers.MessageRepository, tc mainflux.ThingsServiceClient) *httptest.Server { +func newServer(repo readers.MessageRepository, tc readers.Auth) *httptest.Server { mux := api.MakeHandler(repo, tc, svcName) return httptest.NewServer(mux) } @@ -123,8 +123,10 @@ func TestReadAll(t *testing.T) { } svc := mocks.NewThingsService() + svcUsr := authmocks.NewAuthService(map[string]string{"user@example.com": "user@example.com"}) repo := mocks.NewMessageRepository(chanID, fromSenml(messages)) - ts := newServer(repo, svc) + au := readers.NewAuthService(svc, svcUsr) + ts := newServer(repo, au) defer ts.Close() cases := []struct { diff --git a/readers/api/transport.go b/readers/api/transport.go index a244e7bc77..676a9c35ce 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -8,6 +8,7 @@ import ( "encoding/json" "net/http" "strconv" + "strings" "time" kithttp "github.com/go-kit/kit/transport/http" @@ -41,10 +42,13 @@ const ( defFormat = "messages" ) -var auth mainflux.ThingsServiceClient +var ( + errUnauthorizedAccess = errors.New("missing or invalid credentials provided") + auth readers.Auth +) // MakeHandler returns a HTTP handler for API endpoints. -func MakeHandler(svc readers.MessageRepository, tc mainflux.ThingsServiceClient, svcName string) http.Handler { +func MakeHandler(svc readers.MessageRepository, tc readers.Auth, svcName string) http.Handler { auth = tc opts := []kithttp.ServerOption{ @@ -207,17 +211,39 @@ func encodeError(_ context.Context, err error, w http.ResponseWriter) { } } -func authorize(r *http.Request, chanID string) error { +func authorize(r *http.Request, chanID string) (err error) { token := r.Header.Get("Authorization") if token == "" { return errors.ErrAuthentication } + if strings.Contains(token, "Bearer ") { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + token = strings.ReplaceAll(token, "Bearer ", "") + user, err := auth.Identify(ctx, &mainflux.Token{Value: token}) + if err != nil { + e, ok := status.FromError(err) + if ok && e.Code() == codes.PermissionDenied { + return errUnauthorizedAccess + } + return err + } + _, err = auth.IsChannelOwner(ctx, &mainflux.ChannelOwnerReq{Owner: user.Email, ChanID: chanID}) + if err != nil { + e, ok := status.FromError(err) + if ok && e.Code() == codes.PermissionDenied { + return errUnauthorizedAccess + } + return err + } + return nil + } ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - _, err := auth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: chanID}) - if err != nil { + if _, err := auth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: chanID}); err != nil { e, ok := status.FromError(err) if ok && e.Code() == codes.PermissionDenied { return errors.ErrAuthorization diff --git a/readers/auth.go b/readers/auth.go new file mode 100644 index 0000000000..7aa442e309 --- /dev/null +++ b/readers/auth.go @@ -0,0 +1,44 @@ +package readers + +import ( + "context" + + "github.com/golang/protobuf/ptypes/empty" + "github.com/mainflux/mainflux" + "google.golang.org/grpc" +) + +type authService struct { + ts mainflux.ThingsServiceClient + us mainflux.AuthServiceClient +} + +type Auth interface { + Identify(ctx context.Context, in *mainflux.Token, opts ...grpc.CallOption) (*mainflux.UserIdentity, error) + CanAccessByKey(ctx context.Context, in *mainflux.AccessByKeyReq, opts ...grpc.CallOption) (*mainflux.ThingID, error) + IsChannelOwner(ctx context.Context, in *mainflux.ChannelOwnerReq, opts ...grpc.CallOption) (*empty.Empty, error) + CanAccessByID(ctx context.Context, in *mainflux.AccessByIDReq, opts ...grpc.CallOption) (*empty.Empty, error) +} + +func NewAuthService(ts mainflux.ThingsServiceClient, auth mainflux.AuthServiceClient) Auth { + return &authService{ + ts: ts, + us: auth, + } +} + +func (as *authService) Identify(ctx context.Context, in *mainflux.Token, opts ...grpc.CallOption) (*mainflux.UserIdentity, error) { + return as.us.Identify(ctx, in, opts...) +} + +func (as *authService) CanAccessByKey(ctx context.Context, in *mainflux.AccessByKeyReq, opts ...grpc.CallOption) (*mainflux.ThingID, error) { + return as.ts.CanAccessByKey(ctx, in, opts...) +} + +func (as *authService) CanAccessByID(ctx context.Context, in *mainflux.AccessByIDReq, opts ...grpc.CallOption) (*empty.Empty, error) { + return as.ts.CanAccessByID(ctx, in, opts...) +} + +func (as *authService) IsChannelOwner(ctx context.Context, in *mainflux.ChannelOwnerReq, opts ...grpc.CallOption) (*empty.Empty, error) { + return as.ts.IsChannelOwner(ctx, in, opts...) +} From 5c8303f079a9ce5ae25654e9a97930cf3537fdc3 Mon Sep 17 00:00:00 2001 From: mteodor Date: Wed, 6 Oct 2021 13:45:30 +0200 Subject: [PATCH 04/39] enable mongodb reader for user token reading Signed-off-by: mteodor --- cmd/mongodb-reader/main.go | 45 ++++++++++++++++++++++++++++++++++++-- readers/api/transport.go | 3 +-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/cmd/mongodb-reader/main.go b/cmd/mongodb-reader/main.go index ce16c801f0..9e7d6acb3a 100644 --- a/cmd/mongodb-reader/main.go +++ b/cmd/mongodb-reader/main.go @@ -18,6 +18,7 @@ import ( kitprometheus "github.com/go-kit/kit/metrics/prometheus" "github.com/mainflux/mainflux" + authapi "github.com/mainflux/mainflux/auth/api/grpc" "github.com/mainflux/mainflux/logger" "github.com/mainflux/mainflux/readers" "github.com/mainflux/mainflux/readers/api" @@ -44,7 +45,9 @@ const ( defServerKey = "" defJaegerURL = "" defThingsAuthURL = "localhost:8181" + defUsersAuthURL = "localhost:8181" defThingsAuthTimeout = "1s" + defUsersAuthTimeout = "1s" envLogLevel = "MF_MONGO_READER_LOG_LEVEL" envPort = "MF_MONGO_READER_PORT" @@ -72,7 +75,9 @@ type config struct { serverKey string jaegerURL string thingsAuthURL string + usersAuthURL string thingsAuthTimeout time.Duration + usersAuthTimeout time.Duration } func main() { @@ -90,6 +95,16 @@ func main() { tc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsAuthTimeout) + authTracer, authCloser := initJaeger("auth", cfg.jaegerURL, logger) + defer authCloser.Close() + + authConn := connectToAuth(cfg, logger) + defer authConn.Close() + + auth := authapi.NewClient(authTracer, authConn, cfg.usersAuthTimeout) + + authReader := readers.NewAuthService(tc, auth) + db := connectToMongoDB(cfg.dbHost, cfg.dbPort, cfg.dbName, logger) repo := newService(db, logger) @@ -101,12 +116,38 @@ func main() { errs <- fmt.Errorf("%s", <-c) }() - go startHTTPServer(repo, tc, cfg, logger, errs) + go startHTTPServer(repo, authReader, cfg, logger, errs) err = <-errs logger.Error(fmt.Sprintf("MongoDB reader service terminated: %s", err)) } +func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { + var opts []grpc.DialOption + logger.Info("connecting to auth via gRPC") + if cfg.clientTLS { + if cfg.caCerts != "" { + tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "") + if err != nil { + logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err)) + os.Exit(1) + } + opts = append(opts, grpc.WithTransportCredentials(tpc)) + } + } else { + opts = append(opts, grpc.WithInsecure()) + logger.Info("gRPC communication is not encrypted") + } + + conn, err := grpc.Dial(cfg.usersAuthURL, opts...) + if err != nil { + logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err)) + os.Exit(1) + } + + return conn +} + func loadConfigs() config { tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS)) if err != nil { @@ -215,7 +256,7 @@ func newService(db *mongo.Database, logger logger.Logger) readers.MessageReposit return repo } -func startHTTPServer(repo readers.MessageRepository, tc mainflux.ThingsServiceClient, cfg config, logger logger.Logger, errs chan error) { +func startHTTPServer(repo readers.MessageRepository, tc readers.Auth, cfg config, logger logger.Logger, errs chan error) { p := fmt.Sprintf(":%s", cfg.port) if cfg.serverCert != "" || cfg.serverKey != "" { logger.Info(fmt.Sprintf("Mongo reader service started using https on port %s with cert %s key %s", diff --git a/readers/api/transport.go b/readers/api/transport.go index 676a9c35ce..e9f146cb7f 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -216,7 +216,6 @@ func authorize(r *http.Request, chanID string) (err error) { if token == "" { return errors.ErrAuthentication } - if strings.Contains(token, "Bearer ") { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() @@ -228,7 +227,7 @@ func authorize(r *http.Request, chanID string) (err error) { if ok && e.Code() == codes.PermissionDenied { return errUnauthorizedAccess } - return err + return errUnauthorizedAccess } _, err = auth.IsChannelOwner(ctx, &mainflux.ChannelOwnerReq{Owner: user.Email, ChanID: chanID}) if err != nil { From a1b83c0c4391cca42c015fce8706d6e16475b2e7 Mon Sep 17 00:00:00 2001 From: mteodor Date: Wed, 6 Oct 2021 14:30:42 +0200 Subject: [PATCH 05/39] use uuid check for auth switch between thing key and user tok Signed-off-by: mteodor --- readers/api/transport.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/readers/api/transport.go b/readers/api/transport.go index e9f146cb7f..f3e7c00978 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -8,11 +8,11 @@ import ( "encoding/json" "net/http" "strconv" - "strings" "time" kithttp "github.com/go-kit/kit/transport/http" "github.com/go-zoo/bone" + "github.com/gofrs/uuid" "github.com/mainflux/mainflux" "github.com/mainflux/mainflux/internal/httputil" "github.com/mainflux/mainflux/pkg/errors" @@ -44,6 +44,7 @@ const ( var ( errUnauthorizedAccess = errors.New("missing or invalid credentials provided") + errTokenNotBearer = errors.New("authentication scheme must be Bearer") auth readers.Auth ) @@ -216,11 +217,16 @@ func authorize(r *http.Request, chanID string) (err error) { if token == "" { return errors.ErrAuthentication } - if strings.Contains(token, "Bearer ") { + // if strings.Contains(token, "Bearer ") { + // token = strings.ReplaceAll(token, "Bearer ", "") + // } else { + // return errTokenNotBearer + // } + + if uuid.FromStringOrNil(token) == uuid.Nil { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - token = strings.ReplaceAll(token, "Bearer ", "") user, err := auth.Identify(ctx, &mainflux.Token{Value: token}) if err != nil { e, ok := status.FromError(err) @@ -239,6 +245,7 @@ func authorize(r *http.Request, chanID string) (err error) { } return nil } + ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() From d43dbb8f3ec028feb2979f0f49b722b3a7c2b33a Mon Sep 17 00:00:00 2001 From: mteodor Date: Wed, 6 Oct 2021 14:41:56 +0200 Subject: [PATCH 06/39] enable user token reading Signed-off-by: mteodor --- cmd/cassandra-reader/main.go | 49 +++++++++++++++++++++++++++++++++--- cmd/influxdb-reader/main.go | 1 - cmd/postgres-reader/main.go | 48 ++++++++++++++++++++++++++++++++--- 3 files changed, 90 insertions(+), 8 deletions(-) diff --git a/cmd/cassandra-reader/main.go b/cmd/cassandra-reader/main.go index 34adafee1b..830d6c2a0e 100644 --- a/cmd/cassandra-reader/main.go +++ b/cmd/cassandra-reader/main.go @@ -19,6 +19,7 @@ import ( kitprometheus "github.com/go-kit/kit/metrics/prometheus" "github.com/gocql/gocql" "github.com/mainflux/mainflux" + authapi "github.com/mainflux/mainflux/auth/api/grpc" "github.com/mainflux/mainflux/logger" "github.com/mainflux/mainflux/readers" "github.com/mainflux/mainflux/readers/api" @@ -48,6 +49,7 @@ const ( defJaegerURL = "" defThingsAuthURL = "localhost:8181" defThingsAuthTimeout = "1s" + defUsersAuthTimeout = "1s" envLogLevel = "MF_CASSANDRA_READER_LOG_LEVEL" envPort = "MF_CASSANDRA_READER_PORT" @@ -63,6 +65,7 @@ const ( envJaegerURL = "MF_JAEGER_URL" envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL" envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT" + envUsersAuthTimeout = "MF_AUTH_GRPC_TIMEOUT" ) type config struct { @@ -75,7 +78,9 @@ type config struct { serverKey string jaegerURL string thingsAuthURL string + usersAuthURL string thingsAuthTimeout time.Duration + usersAuthTimeout time.Duration } func main() { @@ -96,11 +101,21 @@ func main() { defer thingsCloser.Close() tc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsAuthTimeout) + authTracer, authCloser := initJaeger("auth", cfg.jaegerURL, logger) + defer authCloser.Close() + + authConn := connectToAuth(cfg, logger) + defer authConn.Close() + + auth := authapi.NewClient(authTracer, authConn, cfg.usersAuthTimeout) + + authReader := readers.NewAuthService(tc, auth) + repo := newService(session, logger) errs := make(chan error, 2) - go startHTTPServer(repo, tc, cfg, errs, logger) + go startHTTPServer(repo, authReader, cfg, errs, logger) go func() { c := make(chan os.Signal) @@ -112,6 +127,32 @@ func main() { logger.Error(fmt.Sprintf("Cassandra reader service terminated: %s", err)) } +func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { + var opts []grpc.DialOption + logger.Info("connecting to auth via gRPC") + if cfg.clientTLS { + if cfg.caCerts != "" { + tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "") + if err != nil { + logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err)) + os.Exit(1) + } + opts = append(opts, grpc.WithTransportCredentials(tpc)) + } + } else { + opts = append(opts, grpc.WithInsecure()) + logger.Info("gRPC communication is not encrypted") + } + + conn, err := grpc.Dial(cfg.usersAuthURL, opts...) + if err != nil { + logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err)) + os.Exit(1) + } + + return conn +} + func loadConfig() config { dbPort, err := strconv.Atoi(mainflux.Env(envDBPort, defDBPort)) if err != nil { @@ -230,14 +271,14 @@ func newService(session *gocql.Session, logger logger.Logger) readers.MessageRep return repo } -func startHTTPServer(repo readers.MessageRepository, tc mainflux.ThingsServiceClient, cfg config, errs chan error, logger logger.Logger) { +func startHTTPServer(repo readers.MessageRepository, auth readers.Auth, cfg config, errs chan error, logger logger.Logger) { p := fmt.Sprintf(":%s", cfg.port) if cfg.serverCert != "" || cfg.serverKey != "" { logger.Info(fmt.Sprintf("Cassandra reader service started using https on port %s with cert %s key %s", cfg.port, cfg.serverCert, cfg.serverKey)) - errs <- http.ListenAndServeTLS(p, cfg.serverCert, cfg.serverKey, api.MakeHandler(repo, tc, "cassandra-reader")) + errs <- http.ListenAndServeTLS(p, cfg.serverCert, cfg.serverKey, api.MakeHandler(repo, auth, "cassandra-reader")) return } logger.Info(fmt.Sprintf("Cassandra reader service started, exposed port %s", cfg.port)) - errs <- http.ListenAndServe(p, api.MakeHandler(repo, tc, "cassandra-reader")) + errs <- http.ListenAndServe(p, api.MakeHandler(repo, auth, "cassandra-reader")) } diff --git a/cmd/influxdb-reader/main.go b/cmd/influxdb-reader/main.go index 809f27a5e7..ac09be184d 100644 --- a/cmd/influxdb-reader/main.go +++ b/cmd/influxdb-reader/main.go @@ -21,7 +21,6 @@ import ( "github.com/mainflux/mainflux/readers/api" "github.com/mainflux/mainflux/readers/influxdb" thingsapi "github.com/mainflux/mainflux/things/api/auth/grpc" - opentracing "github.com/opentracing/opentracing-go" stdprometheus "github.com/prometheus/client_golang/prometheus" jconfig "github.com/uber/jaeger-client-go/config" diff --git a/cmd/postgres-reader/main.go b/cmd/postgres-reader/main.go index f94acbbfc6..c41479c506 100644 --- a/cmd/postgres-reader/main.go +++ b/cmd/postgres-reader/main.go @@ -18,6 +18,7 @@ import ( kitprometheus "github.com/go-kit/kit/metrics/prometheus" "github.com/jmoiron/sqlx" "github.com/mainflux/mainflux" + authapi "github.com/mainflux/mainflux/auth/api/grpc" "github.com/mainflux/mainflux/logger" "github.com/mainflux/mainflux/readers" "github.com/mainflux/mainflux/readers/api" @@ -50,6 +51,7 @@ const ( defJaegerURL = "" defThingsAuthURL = "localhost:8181" defThingsAuthTimeout = "1s" + defUsersAuthTimeout = "1s" envLogLevel = "MF_POSTGRES_READER_LOG_LEVEL" envPort = "MF_POSTGRES_READER_PORT" @@ -67,6 +69,8 @@ const ( envJaegerURL = "MF_JAEGER_URL" envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL" envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT" + envAuthURL = "MF_AUTH_GRPC_URL" + envUsersAuthTimeout = "MF_AUTH_GRPC_TIMEOUT" ) type config struct { @@ -77,7 +81,9 @@ type config struct { dbConfig postgres.Config jaegerURL string thingsAuthURL string + usersAuthURL string thingsAuthTimeout time.Duration + usersAuthTimeout time.Duration } func main() { @@ -96,6 +102,16 @@ func main() { tc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsAuthTimeout) + authTracer, authCloser := initJaeger("auth", cfg.jaegerURL, logger) + defer authCloser.Close() + + authConn := connectToAuth(cfg, logger) + defer authConn.Close() + + auth := authapi.NewClient(authTracer, authConn, cfg.usersAuthTimeout) + + authReader := readers.NewAuthService(tc, auth) + db := connectToDB(cfg.dbConfig, logger) defer db.Close() @@ -103,7 +119,7 @@ func main() { errs := make(chan error, 2) - go startHTTPServer(repo, tc, cfg.port, logger, errs) + go startHTTPServer(repo, authReader, cfg.port, logger, errs) go func() { c := make(chan os.Signal) @@ -115,6 +131,32 @@ func main() { logger.Error(fmt.Sprintf("Postgres reader service terminated: %s", err)) } +func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { + var opts []grpc.DialOption + logger.Info("connecting to auth via gRPC") + if cfg.clientTLS { + if cfg.caCerts != "" { + tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "") + if err != nil { + logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err)) + os.Exit(1) + } + opts = append(opts, grpc.WithTransportCredentials(tpc)) + } + } else { + opts = append(opts, grpc.WithInsecure()) + logger.Info("gRPC communication is not encrypted") + } + + conn, err := grpc.Dial(cfg.usersAuthURL, opts...) + if err != nil { + logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err)) + os.Exit(1) + } + + return conn +} + func loadConfig() config { dbConfig := postgres.Config{ Host: mainflux.Env(envDBHost, defDBHost), @@ -229,8 +271,8 @@ func newService(db *sqlx.DB, logger logger.Logger) readers.MessageRepository { return svc } -func startHTTPServer(repo readers.MessageRepository, tc mainflux.ThingsServiceClient, port string, logger logger.Logger, errs chan error) { +func startHTTPServer(repo readers.MessageRepository, auth readers.Auth, port string, logger logger.Logger, errs chan error) { p := fmt.Sprintf(":%s", port) logger.Info(fmt.Sprintf("Postgres reader service started, exposed port %s", port)) - errs <- http.ListenAndServe(p, api.MakeHandler(repo, tc, svcName)) + errs <- http.ListenAndServe(p, api.MakeHandler(repo, auth, svcName)) } From 066ece88b81cc94452e4d72b349654c1c0894642 Mon Sep 17 00:00:00 2001 From: mteodor Date: Wed, 6 Oct 2021 14:43:16 +0200 Subject: [PATCH 07/39] revert to correct version Signed-off-by: mteodor --- users/postgres/init.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/users/postgres/init.go b/users/postgres/init.go index 34a15aabdf..821c802ac7 100644 --- a/users/postgres/init.go +++ b/users/postgres/init.go @@ -78,13 +78,6 @@ func migrateDB(db *sqlx.DB) error { `ALTER TABLE IF EXISTS users ADD PRIMARY KEY (id)`, }, }, - { - Id: "users_5", - Up: []string{ - `ALTER TABLE IF EXISTS users DROP CONSTRAINT IF EXISTS users_owner_id_fkey`, - `ALTER TABLE IF EXISTS users DROP COLUMN IF EXISTS owner_id`, - }, - }, }, } From 1f556122fed0797c92dcdbc6a88a4339d3e40c25 Mon Sep 17 00:00:00 2001 From: mteodor Date: Thu, 7 Oct 2021 16:10:31 +0200 Subject: [PATCH 08/39] fix endpoint test, add additional tests Signed-off-by: mteodor --- readers/api/endpoint_test.go | 367 +++++++++++++++++++++++++++++++---- readers/api/transport.go | 44 ++--- readers/mocks/things.go | 21 +- 3 files changed, 365 insertions(+), 67 deletions(-) diff --git a/readers/api/endpoint_test.go b/readers/api/endpoint_test.go index 2f6f0494e3..a5e1aab5d5 100644 --- a/readers/api/endpoint_test.go +++ b/readers/api/endpoint_test.go @@ -24,7 +24,9 @@ import ( const ( svcName = "test-service" - token = "1" + thingToken = "1" + userToken = "token" + email = "user@example.com" invalid = "invalid" numOfMessages = 100 valueFields = 5 @@ -122,10 +124,12 @@ func TestReadAll(t *testing.T) { messages = append(messages, msg) } - svc := mocks.NewThingsService() - svcUsr := authmocks.NewAuthService(map[string]string{"user@example.com": "user@example.com"}) + thSvc := mocks.NewThingsService(map[string]string{email: chanID}) + + usrSvc := authmocks.NewAuthService(map[string]string{userToken: email}) + repo := mocks.NewMessageRepository(chanID, fromSenml(messages)) - au := readers.NewAuthService(svc, svcUsr) + au := readers.NewAuthService(thSvc, usrSvc) ts := newServer(repo, au) defer ts.Close() @@ -140,7 +144,298 @@ func TestReadAll(t *testing.T) { { desc: "read page with valid offset and limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), - token: token, + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(messages)), + Messages: messages[0:10], + }, + }, + { + desc: "read page with valid offset and limit", + url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), + token: userToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(messages)), + Messages: messages[0:10], + }, + }, + { + desc: "read page with negative offset", + url: fmt.Sprintf("%s/channels/%s/messages?offset=-1&limit=10", ts.URL, chanID), + token: thingToken, + status: http.StatusBadRequest, + }, + { + desc: "read page with negative limit", + url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=-10", ts.URL, chanID), + token: thingToken, + status: http.StatusBadRequest, + }, + { + desc: "read page with zero limit", + url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=0", ts.URL, chanID), + token: thingToken, + status: http.StatusBadRequest, + }, + { + desc: "read page with non-integer offset", + url: fmt.Sprintf("%s/channels/%s/messages?offset=abc&limit=10", ts.URL, chanID), + token: thingToken, + status: http.StatusBadRequest, + }, + { + desc: "read page with non-integer limit", + url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=abc", ts.URL, chanID), + token: thingToken, + status: http.StatusBadRequest, + }, + { + desc: "read page with invalid channel id", + url: fmt.Sprintf("%s/channels//messages?offset=0&limit=10", ts.URL), + token: thingToken, + status: http.StatusBadRequest, + }, + { + desc: "read page with invalid token", + url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), + token: invalid, + status: http.StatusForbidden, + }, + { + desc: "read page with multiple offset", + url: fmt.Sprintf("%s/channels/%s/messages?offset=0&offset=1&limit=10", ts.URL, chanID), + token: thingToken, + status: http.StatusBadRequest, + }, + { + desc: "read page with multiple limit", + url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=20&limit=10", ts.URL, chanID), + token: thingToken, + status: http.StatusBadRequest, + }, + { + desc: "read page with empty token", + url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), + token: "", + status: http.StatusForbidden, + }, + { + desc: "read page with default offset", + url: fmt.Sprintf("%s/channels/%s/messages?limit=10", ts.URL, chanID), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(messages)), + Messages: messages[0:10], + }, + }, + { + desc: "read page with default limit", + url: fmt.Sprintf("%s/channels/%s/messages?offset=0", ts.URL, chanID), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(messages)), + Messages: messages[0:10], + }, + }, + { + desc: "read page with senml fornat", + url: fmt.Sprintf("%s/channels/%s/messages?format=messages", ts.URL, chanID), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(messages)), + Messages: messages[0:10], + }, + }, + { + desc: "read page with subtopic", + url: fmt.Sprintf("%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, chanID, subtopic, httpProt), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(queryMsgs)), + Messages: queryMsgs[0:10], + }, + }, + { + desc: "read page with subtopic and protocol", + url: fmt.Sprintf("%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, chanID, subtopic, httpProt), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(queryMsgs)), + Messages: queryMsgs[0:10], + }, + }, + { + desc: "read page with publisher", + url: fmt.Sprintf("%s/channels/%s/messages?publisher=%s", ts.URL, chanID, pubID2), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(queryMsgs)), + Messages: queryMsgs[0:10], + }, + }, + { + desc: "read page with protocol", + url: fmt.Sprintf("%s/channels/%s/messages?protocol=http", ts.URL, chanID), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(queryMsgs)), + Messages: queryMsgs[0:10], + }, + }, + { + desc: "read page with name", + url: fmt.Sprintf("%s/channels/%s/messages?name=%s", ts.URL, chanID, msgName), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(queryMsgs)), + Messages: queryMsgs[0:10], + }, + }, + { + desc: "read page with value", + url: fmt.Sprintf("%s/channels/%s/messages?v=%f", ts.URL, chanID, v), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(valueMsgs)), + Messages: valueMsgs[0:10], + }, + }, + { + desc: "read page with value and equal comparator", + url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v, readers.EqualKey), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(valueMsgs)), + Messages: valueMsgs[0:10], + }, + }, + { + desc: "read page with value and lower-than comparator", + url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v+1, readers.LowerThanKey), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(valueMsgs)), + Messages: valueMsgs[0:10], + }, + }, + { + desc: "read page with value and lower-than-or-equal comparator", + url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v+1, readers.LowerThanEqualKey), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(valueMsgs)), + Messages: valueMsgs[0:10], + }, + }, + { + desc: "read page with value and greater-than comparator", + url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v-1, readers.GreaterThanKey), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(valueMsgs)), + Messages: valueMsgs[0:10], + }, + }, + { + desc: "read page with value and greater-than-or-equal comparator", + url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v-1, readers.GreaterThanEqualKey), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(valueMsgs)), + Messages: valueMsgs[0:10], + }, + }, + { + desc: "read page with non-float value", + url: fmt.Sprintf("%s/channels/%s/messages?v=ab01", ts.URL, chanID), + token: thingToken, + status: http.StatusBadRequest, + }, + { + desc: "read page with value and wrong comparator", + url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=wrong", ts.URL, chanID, v-1), + token: thingToken, + status: http.StatusBadRequest, + }, + { + desc: "read page with boolean value", + url: fmt.Sprintf("%s/channels/%s/messages?vb=true", ts.URL, chanID), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(boolMsgs)), + Messages: boolMsgs[0:10], + }, + }, + { + desc: "read page with non-boolean value", + url: fmt.Sprintf("%s/channels/%s/messages?vb=yes", ts.URL, chanID), + token: thingToken, + status: http.StatusBadRequest, + }, + { + desc: "read page with string value", + url: fmt.Sprintf("%s/channels/%s/messages?vs=%s", ts.URL, chanID, vs), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(stringMsgs)), + Messages: stringMsgs[0:10], + }, + }, + { + desc: "read page with data value", + url: fmt.Sprintf("%s/channels/%s/messages?vd=%s", ts.URL, chanID, vd), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(dataMsgs)), + Messages: dataMsgs[0:10], + }, + }, + { + desc: "read page with non-float from", + url: fmt.Sprintf("%s/channels/%s/messages?from=ABCD", ts.URL, chanID), + token: thingToken, + status: http.StatusBadRequest, + }, + + { + desc: "read page with non-float to", + url: fmt.Sprintf("%s/channels/%s/messages?to=ABCD", ts.URL, chanID), + token: thingToken, + status: http.StatusBadRequest, + }, + { + desc: "read page with from/to", + url: fmt.Sprintf("%s/channels/%s/messages?from=%f&to=%f", ts.URL, chanID, messages[19].Time, messages[4].Time), + token: thingToken, + status: http.StatusOK, + res: pageRes{ + Total: uint64(len(messages[5:20])), + Messages: messages[5:15], + }, + }, + { + desc: "read page with valid offset and limit", + url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(messages)), @@ -150,37 +445,37 @@ func TestReadAll(t *testing.T) { { desc: "read page with negative offset", url: fmt.Sprintf("%s/channels/%s/messages?offset=-1&limit=10", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusBadRequest, }, { desc: "read page with negative limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=-10", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusBadRequest, }, { desc: "read page with zero limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=0", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusBadRequest, }, { desc: "read page with non-integer offset", url: fmt.Sprintf("%s/channels/%s/messages?offset=abc&limit=10", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusBadRequest, }, { desc: "read page with non-integer limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=abc", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusBadRequest, }, { desc: "read page with invalid channel id", url: fmt.Sprintf("%s/channels//messages?offset=0&limit=10", ts.URL), - token: token, + token: userToken, status: http.StatusBadRequest, }, { @@ -192,13 +487,13 @@ func TestReadAll(t *testing.T) { { desc: "read page with multiple offset", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&offset=1&limit=10", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusBadRequest, }, { desc: "read page with multiple limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=20&limit=10", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusBadRequest, }, { @@ -210,7 +505,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with default offset", url: fmt.Sprintf("%s/channels/%s/messages?limit=10", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(messages)), @@ -220,7 +515,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with default limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(messages)), @@ -230,7 +525,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with senml fornat", url: fmt.Sprintf("%s/channels/%s/messages?format=messages", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(messages)), @@ -240,7 +535,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with subtopic", url: fmt.Sprintf("%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, chanID, subtopic, httpProt), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(queryMsgs)), @@ -250,7 +545,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with subtopic and protocol", url: fmt.Sprintf("%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, chanID, subtopic, httpProt), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(queryMsgs)), @@ -260,7 +555,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with publisher", url: fmt.Sprintf("%s/channels/%s/messages?publisher=%s", ts.URL, chanID, pubID2), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(queryMsgs)), @@ -270,7 +565,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with protocol", url: fmt.Sprintf("%s/channels/%s/messages?protocol=http", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(queryMsgs)), @@ -280,7 +575,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with name", url: fmt.Sprintf("%s/channels/%s/messages?name=%s", ts.URL, chanID, msgName), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(queryMsgs)), @@ -290,7 +585,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value", url: fmt.Sprintf("%s/channels/%s/messages?v=%f", ts.URL, chanID, v), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -300,7 +595,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value and equal comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v, readers.EqualKey), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -310,7 +605,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value and lower-than comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v+1, readers.LowerThanKey), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -320,7 +615,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value and lower-than-or-equal comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v+1, readers.LowerThanEqualKey), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -330,7 +625,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value and greater-than comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v-1, readers.GreaterThanKey), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -340,7 +635,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value and greater-than-or-equal comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v-1, readers.GreaterThanEqualKey), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -350,19 +645,19 @@ func TestReadAll(t *testing.T) { { desc: "read page with non-float value", url: fmt.Sprintf("%s/channels/%s/messages?v=ab01", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusBadRequest, }, { desc: "read page with value and wrong comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=wrong", ts.URL, chanID, v-1), - token: token, + token: userToken, status: http.StatusBadRequest, }, { desc: "read page with boolean value", url: fmt.Sprintf("%s/channels/%s/messages?vb=true", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(boolMsgs)), @@ -372,13 +667,13 @@ func TestReadAll(t *testing.T) { { desc: "read page with non-boolean value", url: fmt.Sprintf("%s/channels/%s/messages?vb=yes", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusBadRequest, }, { desc: "read page with string value", url: fmt.Sprintf("%s/channels/%s/messages?vs=%s", ts.URL, chanID, vs), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(stringMsgs)), @@ -388,7 +683,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with data value", url: fmt.Sprintf("%s/channels/%s/messages?vd=%s", ts.URL, chanID, vd), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(dataMsgs)), @@ -398,20 +693,20 @@ func TestReadAll(t *testing.T) { { desc: "read page with non-float from", url: fmt.Sprintf("%s/channels/%s/messages?from=ABCD", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusBadRequest, }, { desc: "read page with non-float to", url: fmt.Sprintf("%s/channels/%s/messages?to=ABCD", ts.URL, chanID), - token: token, + token: userToken, status: http.StatusBadRequest, }, { desc: "read page with from/to", url: fmt.Sprintf("%s/channels/%s/messages?from=%f&to=%f", ts.URL, chanID, messages[19].Time, messages[4].Time), - token: token, + token: userToken, status: http.StatusOK, res: pageRes{ Total: uint64(len(messages[5:20])), diff --git a/readers/api/transport.go b/readers/api/transport.go index f3e7c00978..68a25b60e4 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -6,13 +6,13 @@ package api import ( "context" "encoding/json" + "fmt" "net/http" "strconv" "time" kithttp "github.com/go-kit/kit/transport/http" "github.com/go-zoo/bone" - "github.com/gofrs/uuid" "github.com/mainflux/mainflux" "github.com/mainflux/mainflux/internal/httputil" "github.com/mainflux/mainflux/pkg/errors" @@ -223,41 +223,33 @@ func authorize(r *http.Request, chanID string) (err error) { // return errTokenNotBearer // } - if uuid.FromStringOrNil(token) == uuid.Nil { - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() - user, err := auth.Identify(ctx, &mainflux.Token{Value: token}) - if err != nil { - e, ok := status.FromError(err) - if ok && e.Code() == codes.PermissionDenied { - return errUnauthorizedAccess - } - return errUnauthorizedAccess - } - _, err = auth.IsChannelOwner(ctx, &mainflux.ChannelOwnerReq{Owner: user.Email, ChanID: chanID}) - if err != nil { - e, ok := status.FromError(err) - if ok && e.Code() == codes.PermissionDenied { - return errUnauthorizedAccess - } - return err - } + t, err := auth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: chanID}) + if err == nil { return nil } + fmt.Println(t) - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - - if _, err := auth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: chanID}); err != nil { + user, err := auth.Identify(ctx, &mainflux.Token{Value: token}) + if err != nil { e, ok := status.FromError(err) if ok && e.Code() == codes.PermissionDenied { - return errors.ErrAuthorization + return errUnauthorizedAccess + } + return errUnauthorizedAccess + } + _, err = auth.IsChannelOwner(ctx, &mainflux.ChannelOwnerReq{Owner: user.Email, ChanID: chanID}) + if err != nil { + e, ok := status.FromError(err) + if ok && e.Code() == codes.PermissionDenied { + return errUnauthorizedAccess } return err } - return nil + } func readBoolValueQuery(r *http.Request, key string) (bool, error) { diff --git a/readers/mocks/things.go b/readers/mocks/things.go index f9b668df37..e3c4636703 100644 --- a/readers/mocks/things.go +++ b/readers/mocks/things.go @@ -15,11 +15,13 @@ import ( var _ mainflux.ThingsServiceClient = (*thingsServiceMock)(nil) -type thingsServiceMock struct{} +type thingsServiceMock struct { + channels map[string]string +} // NewThingsService returns mock implementation of things service -func NewThingsService() mainflux.ThingsServiceClient { - return thingsServiceMock{} +func NewThingsService(channels map[string]string) mainflux.ThingsServiceClient { + return &thingsServiceMock{channels} } func (svc thingsServiceMock) CanAccessByKey(ctx context.Context, in *mainflux.AccessByKeyReq, opts ...grpc.CallOption) (*mainflux.ThingID, error) { @@ -32,6 +34,10 @@ func (svc thingsServiceMock) CanAccessByKey(ctx context.Context, in *mainflux.Ac return nil, errors.ErrAuthentication } + if token == "token" { + return nil, errUnauthorized + } + return &mainflux.ThingID{Value: token}, nil } @@ -39,8 +45,13 @@ func (svc thingsServiceMock) CanAccessByID(context.Context, *mainflux.AccessByID panic("not implemented") } -func (svc thingsServiceMock) IsChannelOwner(context.Context, *mainflux.ChannelOwnerReq, ...grpc.CallOption) (*empty.Empty, error) { - panic("not implemented") +func (svc thingsServiceMock) IsChannelOwner(ctx context.Context, in *mainflux.ChannelOwnerReq, opts ...grpc.CallOption) (*empty.Empty, error) { + if id, ok := svc.channels[in.GetOwner()]; ok { + if id == in.ChanID { + return nil, nil + } + } + return nil, users.ErrUnauthorizedAccess } func (svc thingsServiceMock) Identify(context.Context, *mainflux.Token, ...grpc.CallOption) (*mainflux.ThingID, error) { From 8cadbb5e7ebc86c4061589ec3ebfc511488e4214 Mon Sep 17 00:00:00 2001 From: mteodor Date: Fri, 8 Oct 2021 10:11:19 +0200 Subject: [PATCH 09/39] remove logs,dead code Signed-off-by: mteodor --- cmd/mongodb-reader/main.go | 4 ++-- readers/api/transport.go | 21 +++++---------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/cmd/mongodb-reader/main.go b/cmd/mongodb-reader/main.go index 9e7d6acb3a..802a0c9174 100644 --- a/cmd/mongodb-reader/main.go +++ b/cmd/mongodb-reader/main.go @@ -124,7 +124,7 @@ func main() { func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { var opts []grpc.DialOption - logger.Info("connecting to auth via gRPC") + logger.Info("Connecting to auth via gRPC") if cfg.clientTLS { if cfg.caCerts != "" { tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "") @@ -144,7 +144,7 @@ func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err)) os.Exit(1) } - + logger.Info("Established gRPC connection to auth via gRPC") return conn } diff --git a/readers/api/transport.go b/readers/api/transport.go index 68a25b60e4..fc2bb5685d 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -6,10 +6,8 @@ package api import ( "context" "encoding/json" - "fmt" "net/http" "strconv" - "time" kithttp "github.com/go-kit/kit/transport/http" "github.com/go-zoo/bone" @@ -70,13 +68,13 @@ func MakeHandler(svc readers.MessageRepository, tc readers.Auth, svcName string) return mux } -func decodeList(_ context.Context, r *http.Request) (interface{}, error) { +func decodeList(ctx context.Context, r *http.Request) (interface{}, error) { chanID := bone.GetValue(r, "chanID") if chanID == "" { return nil, errors.ErrInvalidQueryParams } - if err := authorize(r, chanID); err != nil { + if err := authorize(ctx, r, chanID); err != nil { return nil, err } @@ -212,25 +210,15 @@ func encodeError(_ context.Context, err error, w http.ResponseWriter) { } } -func authorize(r *http.Request, chanID string) (err error) { +func authorize(ctx context.Context, r *http.Request, chanID string) (err error) { token := r.Header.Get("Authorization") if token == "" { return errors.ErrAuthentication } - // if strings.Contains(token, "Bearer ") { - // token = strings.ReplaceAll(token, "Bearer ", "") - // } else { - // return errTokenNotBearer - // } - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - - t, err := auth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: chanID}) - if err == nil { + if _, err := auth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: chanID}); err == nil { return nil } - fmt.Println(t) user, err := auth.Identify(ctx, &mainflux.Token{Value: token}) if err != nil { @@ -240,6 +228,7 @@ func authorize(r *http.Request, chanID string) (err error) { } return errUnauthorizedAccess } + _, err = auth.IsChannelOwner(ctx, &mainflux.ChannelOwnerReq{Owner: user.Email, ChanID: chanID}) if err != nil { e, ok := status.FromError(err) From 7400a4fe1a413a2456674d19ad8b683722b3b6f0 Mon Sep 17 00:00:00 2001 From: mteodor Date: Fri, 8 Oct 2021 16:24:26 +0200 Subject: [PATCH 10/39] fix logging messages Signed-off-by: mteodor --- cmd/cassandra-reader/main.go | 4 ++-- cmd/influxdb-reader/main.go | 4 ++-- cmd/postgres-reader/main.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/cassandra-reader/main.go b/cmd/cassandra-reader/main.go index 830d6c2a0e..59588622cc 100644 --- a/cmd/cassandra-reader/main.go +++ b/cmd/cassandra-reader/main.go @@ -129,7 +129,7 @@ func main() { func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { var opts []grpc.DialOption - logger.Info("connecting to auth via gRPC") + logger.Info("Connecting to auth via gRPC") if cfg.clientTLS { if cfg.caCerts != "" { tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "") @@ -149,7 +149,7 @@ func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err)) os.Exit(1) } - + logger.Info("Established gRPC connection to auth via gRPC") return conn } diff --git a/cmd/influxdb-reader/main.go b/cmd/influxdb-reader/main.go index ac09be184d..5cb8f8fe6b 100644 --- a/cmd/influxdb-reader/main.go +++ b/cmd/influxdb-reader/main.go @@ -131,7 +131,7 @@ func main() { func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { var opts []grpc.DialOption - logger.Info("connecting to auth via gRPC") + logger.Info("Connecting to auth via gRPC") if cfg.clientTLS { if cfg.caCerts != "" { tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "") @@ -151,7 +151,7 @@ func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err)) os.Exit(1) } - + logger.Info("Established gRPC connection to auth via gRPC") return conn } diff --git a/cmd/postgres-reader/main.go b/cmd/postgres-reader/main.go index c41479c506..78ffe5c7f8 100644 --- a/cmd/postgres-reader/main.go +++ b/cmd/postgres-reader/main.go @@ -133,7 +133,7 @@ func main() { func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { var opts []grpc.DialOption - logger.Info("connecting to auth via gRPC") + logger.Info("Connecting to auth via gRPC") if cfg.clientTLS { if cfg.caCerts != "" { tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "") @@ -153,7 +153,7 @@ func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err)) os.Exit(1) } - + logger.Info("Established gRPC connection to auth via gRPC") return conn } From 1280d1023802d706f1495fe426a93f0858ab01d3 Mon Sep 17 00:00:00 2001 From: mteodor Date: Tue, 19 Oct 2021 17:44:43 +0200 Subject: [PATCH 11/39] remove auth interface, add authorization header type Signed-off-by: mteodor --- cmd/cassandra-reader/main.go | 13 ++-- cmd/influxdb-reader/main.go | 11 ++- cmd/mongodb-reader/main.go | 13 ++-- cmd/postgres-reader/main.go | 11 ++- readers/api/endpoint_test.go | 144 +++++++++++++++++------------------ readers/api/transport.go | 60 +++++++++------ readers/auth.go | 44 ----------- 7 files changed, 132 insertions(+), 164 deletions(-) delete mode 100644 readers/auth.go diff --git a/cmd/cassandra-reader/main.go b/cmd/cassandra-reader/main.go index 59588622cc..5955691e87 100644 --- a/cmd/cassandra-reader/main.go +++ b/cmd/cassandra-reader/main.go @@ -109,13 +109,11 @@ func main() { auth := authapi.NewClient(authTracer, authConn, cfg.usersAuthTimeout) - authReader := readers.NewAuthService(tc, auth) - repo := newService(session, logger) errs := make(chan error, 2) - go startHTTPServer(repo, authReader, cfg, errs, logger) + go startHTTPServer(repo, tc, auth, cfg, errs, logger) go func() { c := make(chan os.Signal) @@ -149,7 +147,7 @@ func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err)) os.Exit(1) } - logger.Info("Established gRPC connection to auth via gRPC") + logger.Info(fmt.Sprintf("Established gRPC connection to things via gRPC: %s", cfg.usersAuthURL)) return conn } @@ -222,6 +220,7 @@ func connectToThings(cfg config, logger logger.Logger) *grpc.ClientConn { logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err)) os.Exit(1) } + logger.Info(fmt.Sprintf("Established gRPC connection to things via gRPC: %s", cfg.thingsAuthURL)) return conn } @@ -271,14 +270,14 @@ func newService(session *gocql.Session, logger logger.Logger) readers.MessageRep return repo } -func startHTTPServer(repo readers.MessageRepository, auth readers.Auth, cfg config, errs chan error, logger logger.Logger) { +func startHTTPServer(repo readers.MessageRepository, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient, cfg config, errs chan error, logger logger.Logger) { p := fmt.Sprintf(":%s", cfg.port) if cfg.serverCert != "" || cfg.serverKey != "" { logger.Info(fmt.Sprintf("Cassandra reader service started using https on port %s with cert %s key %s", cfg.port, cfg.serverCert, cfg.serverKey)) - errs <- http.ListenAndServeTLS(p, cfg.serverCert, cfg.serverKey, api.MakeHandler(repo, auth, "cassandra-reader")) + errs <- http.ListenAndServeTLS(p, cfg.serverCert, cfg.serverKey, api.MakeHandler(repo, tc, ac, "cassandra-reader")) return } logger.Info(fmt.Sprintf("Cassandra reader service started, exposed port %s", cfg.port)) - errs <- http.ListenAndServe(p, api.MakeHandler(repo, auth, "cassandra-reader")) + errs <- http.ListenAndServe(p, api.MakeHandler(repo, tc, ac, "cassandra-reader")) } diff --git a/cmd/influxdb-reader/main.go b/cmd/influxdb-reader/main.go index 5cb8f8fe6b..610c83db31 100644 --- a/cmd/influxdb-reader/main.go +++ b/cmd/influxdb-reader/main.go @@ -105,8 +105,6 @@ func main() { auth := authapi.NewClient(authTracer, authConn, cfg.usersAuthTimeout) - authReader := readers.NewAuthService(tc, auth) - client, err := influxdata.NewHTTPClient(clientCfg) if err != nil { logger.Error(fmt.Sprintf("Failed to create InfluxDB client: %s", err)) @@ -123,7 +121,7 @@ func main() { errs <- fmt.Errorf("%s", <-c) }() - go startHTTPServer(repo, authReader, cfg, logger, errs) + go startHTTPServer(repo, tc, auth, cfg, logger, errs) err = <-errs logger.Error(fmt.Sprintf("InfluxDB writer service terminated: %s", err)) @@ -221,6 +219,7 @@ func connectToThings(cfg config, logger logger.Logger) *grpc.ClientConn { logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err)) os.Exit(1) } + logger.Info(fmt.Sprintf("Established gRPC connection to things via gRPC: %s", cfg.thingsAuthURL)) return conn } @@ -270,14 +269,14 @@ func newService(client influxdata.Client, dbName string, logger logger.Logger) r return repo } -func startHTTPServer(repo readers.MessageRepository, tc readers.Auth, cfg config, logger logger.Logger, errs chan error) { +func startHTTPServer(repo readers.MessageRepository, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient, cfg config, logger logger.Logger, errs chan error) { p := fmt.Sprintf(":%s", cfg.port) if cfg.serverCert != "" || cfg.serverKey != "" { logger.Info(fmt.Sprintf("InfluxDB reader service started using https on port %s with cert %s key %s", cfg.port, cfg.serverCert, cfg.serverKey)) - errs <- http.ListenAndServeTLS(p, cfg.serverCert, cfg.serverKey, api.MakeHandler(repo, tc, "influxdb-reader")) + errs <- http.ListenAndServeTLS(p, cfg.serverCert, cfg.serverKey, api.MakeHandler(repo, tc, ac, "influxdb-reader")) return } logger.Info(fmt.Sprintf("InfluxDB reader service started, exposed port %s", cfg.port)) - errs <- http.ListenAndServe(p, api.MakeHandler(repo, tc, "influxdb-reader")) + errs <- http.ListenAndServe(p, api.MakeHandler(repo, tc, ac, "influxdb-reader")) } diff --git a/cmd/mongodb-reader/main.go b/cmd/mongodb-reader/main.go index 802a0c9174..44befa5a43 100644 --- a/cmd/mongodb-reader/main.go +++ b/cmd/mongodb-reader/main.go @@ -103,8 +103,6 @@ func main() { auth := authapi.NewClient(authTracer, authConn, cfg.usersAuthTimeout) - authReader := readers.NewAuthService(tc, auth) - db := connectToMongoDB(cfg.dbHost, cfg.dbPort, cfg.dbName, logger) repo := newService(db, logger) @@ -116,7 +114,7 @@ func main() { errs <- fmt.Errorf("%s", <-c) }() - go startHTTPServer(repo, authReader, cfg, logger, errs) + go startHTTPServer(repo, tc, auth, cfg, logger, errs) err = <-errs logger.Error(fmt.Sprintf("MongoDB reader service terminated: %s", err)) @@ -144,7 +142,7 @@ func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err)) os.Exit(1) } - logger.Info("Established gRPC connection to auth via gRPC") + logger.Info(fmt.Sprintf("Established gRPC connection to auth via gRPC: %s", cfg.usersAuthURL)) return conn } @@ -231,6 +229,7 @@ func connectToThings(cfg config, logger logger.Logger) *grpc.ClientConn { logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err)) os.Exit(1) } + logger.Info(fmt.Sprintf("Established gRPC connection to things via gRPC: %s", cfg.thingsAuthURL)) return conn } @@ -256,14 +255,14 @@ func newService(db *mongo.Database, logger logger.Logger) readers.MessageReposit return repo } -func startHTTPServer(repo readers.MessageRepository, tc readers.Auth, cfg config, logger logger.Logger, errs chan error) { +func startHTTPServer(repo readers.MessageRepository, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient, cfg config, logger logger.Logger, errs chan error) { p := fmt.Sprintf(":%s", cfg.port) if cfg.serverCert != "" || cfg.serverKey != "" { logger.Info(fmt.Sprintf("Mongo reader service started using https on port %s with cert %s key %s", cfg.port, cfg.serverCert, cfg.serverKey)) - errs <- http.ListenAndServeTLS(p, cfg.serverCert, cfg.serverKey, api.MakeHandler(repo, tc, "mongodb-reader")) + errs <- http.ListenAndServeTLS(p, cfg.serverCert, cfg.serverKey, api.MakeHandler(repo, tc, ac, "mongodb-reader")) return } logger.Info(fmt.Sprintf("Mongo reader service started, exposed port %s", cfg.port)) - errs <- http.ListenAndServe(p, api.MakeHandler(repo, tc, "mongodb-reader")) + errs <- http.ListenAndServe(p, api.MakeHandler(repo, tc, ac, "mongodb-reader")) } diff --git a/cmd/postgres-reader/main.go b/cmd/postgres-reader/main.go index 78ffe5c7f8..1c9b9cfb32 100644 --- a/cmd/postgres-reader/main.go +++ b/cmd/postgres-reader/main.go @@ -110,8 +110,6 @@ func main() { auth := authapi.NewClient(authTracer, authConn, cfg.usersAuthTimeout) - authReader := readers.NewAuthService(tc, auth) - db := connectToDB(cfg.dbConfig, logger) defer db.Close() @@ -119,7 +117,7 @@ func main() { errs := make(chan error, 2) - go startHTTPServer(repo, authReader, cfg.port, logger, errs) + go startHTTPServer(repo, tc, auth, cfg.port, logger, errs) go func() { c := make(chan os.Signal) @@ -153,7 +151,7 @@ func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err)) os.Exit(1) } - logger.Info("Established gRPC connection to auth via gRPC") + logger.Info(fmt.Sprintf("Established gRPC connection to auth via gRPC: %s", cfg.usersAuthURL)) return conn } @@ -246,6 +244,7 @@ func connectToThings(cfg config, logger logger.Logger) *grpc.ClientConn { logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err)) os.Exit(1) } + logger.Info(fmt.Sprintf("Established gRPC connection to things via gRPC: %s", cfg.thingsAuthURL)) return conn } @@ -271,8 +270,8 @@ func newService(db *sqlx.DB, logger logger.Logger) readers.MessageRepository { return svc } -func startHTTPServer(repo readers.MessageRepository, auth readers.Auth, port string, logger logger.Logger, errs chan error) { +func startHTTPServer(repo readers.MessageRepository, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient, port string, logger logger.Logger, errs chan error) { p := fmt.Sprintf(":%s", port) logger.Info(fmt.Sprintf("Postgres reader service started, exposed port %s", port)) - errs <- http.ListenAndServe(p, api.MakeHandler(repo, auth, svcName)) + errs <- http.ListenAndServe(p, api.MakeHandler(repo, tc, ac, svcName)) } diff --git a/readers/api/endpoint_test.go b/readers/api/endpoint_test.go index a5e1aab5d5..c05a612b6a 100644 --- a/readers/api/endpoint_test.go +++ b/readers/api/endpoint_test.go @@ -12,6 +12,7 @@ import ( "testing" "time" + "github.com/mainflux/mainflux" "github.com/mainflux/mainflux/pkg/transformers/senml" "github.com/mainflux/mainflux/pkg/uuid" "github.com/mainflux/mainflux/readers" @@ -46,8 +47,8 @@ var ( idProvider = uuid.New() ) -func newServer(repo readers.MessageRepository, tc readers.Auth) *httptest.Server { - mux := api.MakeHandler(repo, tc, svcName) +func newServer(repo readers.MessageRepository, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient) *httptest.Server { + mux := api.MakeHandler(repo, tc, ac, svcName) return httptest.NewServer(mux) } @@ -129,8 +130,7 @@ func TestReadAll(t *testing.T) { usrSvc := authmocks.NewAuthService(map[string]string{userToken: email}) repo := mocks.NewMessageRepository(chanID, fromSenml(messages)) - au := readers.NewAuthService(thSvc, usrSvc) - ts := newServer(repo, au) + ts := newServer(repo, thSvc, usrSvc) defer ts.Close() cases := []struct { @@ -144,7 +144,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with valid offset and limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(messages)), @@ -154,7 +154,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with valid offset and limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(messages)), @@ -164,55 +164,55 @@ func TestReadAll(t *testing.T) { { desc: "read page with negative offset", url: fmt.Sprintf("%s/channels/%s/messages?offset=-1&limit=10", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { desc: "read page with negative limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=-10", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { desc: "read page with zero limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=0", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { desc: "read page with non-integer offset", url: fmt.Sprintf("%s/channels/%s/messages?offset=abc&limit=10", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { desc: "read page with non-integer limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=abc", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { desc: "read page with invalid channel id", url: fmt.Sprintf("%s/channels//messages?offset=0&limit=10", ts.URL), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { desc: "read page with invalid token", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), - token: invalid, + token: fmt.Sprintf("Thing %s", invalid), status: http.StatusForbidden, }, { desc: "read page with multiple offset", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&offset=1&limit=10", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { desc: "read page with multiple limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=20&limit=10", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { @@ -224,7 +224,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with default offset", url: fmt.Sprintf("%s/channels/%s/messages?limit=10", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(messages)), @@ -234,7 +234,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with default limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(messages)), @@ -244,7 +244,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with senml fornat", url: fmt.Sprintf("%s/channels/%s/messages?format=messages", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(messages)), @@ -254,7 +254,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with subtopic", url: fmt.Sprintf("%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, chanID, subtopic, httpProt), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(queryMsgs)), @@ -264,7 +264,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with subtopic and protocol", url: fmt.Sprintf("%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, chanID, subtopic, httpProt), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(queryMsgs)), @@ -274,7 +274,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with publisher", url: fmt.Sprintf("%s/channels/%s/messages?publisher=%s", ts.URL, chanID, pubID2), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(queryMsgs)), @@ -284,7 +284,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with protocol", url: fmt.Sprintf("%s/channels/%s/messages?protocol=http", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(queryMsgs)), @@ -294,7 +294,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with name", url: fmt.Sprintf("%s/channels/%s/messages?name=%s", ts.URL, chanID, msgName), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(queryMsgs)), @@ -304,7 +304,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value", url: fmt.Sprintf("%s/channels/%s/messages?v=%f", ts.URL, chanID, v), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -314,7 +314,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value and equal comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v, readers.EqualKey), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -324,7 +324,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value and lower-than comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v+1, readers.LowerThanKey), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -334,7 +334,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value and lower-than-or-equal comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v+1, readers.LowerThanEqualKey), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -344,7 +344,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value and greater-than comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v-1, readers.GreaterThanKey), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -354,7 +354,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value and greater-than-or-equal comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v-1, readers.GreaterThanEqualKey), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -364,19 +364,19 @@ func TestReadAll(t *testing.T) { { desc: "read page with non-float value", url: fmt.Sprintf("%s/channels/%s/messages?v=ab01", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { desc: "read page with value and wrong comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=wrong", ts.URL, chanID, v-1), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { desc: "read page with boolean value", url: fmt.Sprintf("%s/channels/%s/messages?vb=true", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(boolMsgs)), @@ -386,13 +386,13 @@ func TestReadAll(t *testing.T) { { desc: "read page with non-boolean value", url: fmt.Sprintf("%s/channels/%s/messages?vb=yes", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { desc: "read page with string value", url: fmt.Sprintf("%s/channels/%s/messages?vs=%s", ts.URL, chanID, vs), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(stringMsgs)), @@ -402,7 +402,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with data value", url: fmt.Sprintf("%s/channels/%s/messages?vd=%s", ts.URL, chanID, vd), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(dataMsgs)), @@ -412,20 +412,20 @@ func TestReadAll(t *testing.T) { { desc: "read page with non-float from", url: fmt.Sprintf("%s/channels/%s/messages?from=ABCD", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { desc: "read page with non-float to", url: fmt.Sprintf("%s/channels/%s/messages?to=ABCD", ts.URL, chanID), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { desc: "read page with from/to", url: fmt.Sprintf("%s/channels/%s/messages?from=%f&to=%f", ts.URL, chanID, messages[19].Time, messages[4].Time), - token: thingToken, + token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(messages[5:20])), @@ -435,7 +435,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with valid offset and limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(messages)), @@ -445,55 +445,55 @@ func TestReadAll(t *testing.T) { { desc: "read page with negative offset", url: fmt.Sprintf("%s/channels/%s/messages?offset=-1&limit=10", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { desc: "read page with negative limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=-10", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { desc: "read page with zero limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=0", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { desc: "read page with non-integer offset", url: fmt.Sprintf("%s/channels/%s/messages?offset=abc&limit=10", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { desc: "read page with non-integer limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=abc", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { desc: "read page with invalid channel id", url: fmt.Sprintf("%s/channels//messages?offset=0&limit=10", ts.URL), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { desc: "read page with invalid token", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), - token: invalid, - status: http.StatusUnauthorized, + token: fmt.Sprintf("Bearer %s", invalid), + status: http.StatusForbidden, }, { desc: "read page with multiple offset", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&offset=1&limit=10", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { desc: "read page with multiple limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=20&limit=10", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { @@ -505,7 +505,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with default offset", url: fmt.Sprintf("%s/channels/%s/messages?limit=10", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(messages)), @@ -515,7 +515,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with default limit", url: fmt.Sprintf("%s/channels/%s/messages?offset=0", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(messages)), @@ -525,7 +525,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with senml fornat", url: fmt.Sprintf("%s/channels/%s/messages?format=messages", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(messages)), @@ -535,7 +535,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with subtopic", url: fmt.Sprintf("%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, chanID, subtopic, httpProt), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(queryMsgs)), @@ -545,7 +545,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with subtopic and protocol", url: fmt.Sprintf("%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, chanID, subtopic, httpProt), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(queryMsgs)), @@ -555,7 +555,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with publisher", url: fmt.Sprintf("%s/channels/%s/messages?publisher=%s", ts.URL, chanID, pubID2), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(queryMsgs)), @@ -565,7 +565,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with protocol", url: fmt.Sprintf("%s/channels/%s/messages?protocol=http", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(queryMsgs)), @@ -575,7 +575,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with name", url: fmt.Sprintf("%s/channels/%s/messages?name=%s", ts.URL, chanID, msgName), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(queryMsgs)), @@ -585,7 +585,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value", url: fmt.Sprintf("%s/channels/%s/messages?v=%f", ts.URL, chanID, v), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -595,7 +595,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value and equal comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v, readers.EqualKey), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -605,7 +605,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value and lower-than comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v+1, readers.LowerThanKey), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -615,7 +615,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value and lower-than-or-equal comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v+1, readers.LowerThanEqualKey), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -625,7 +625,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value and greater-than comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v-1, readers.GreaterThanKey), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -635,7 +635,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with value and greater-than-or-equal comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v-1, readers.GreaterThanEqualKey), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(valueMsgs)), @@ -645,19 +645,19 @@ func TestReadAll(t *testing.T) { { desc: "read page with non-float value", url: fmt.Sprintf("%s/channels/%s/messages?v=ab01", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { desc: "read page with value and wrong comparator", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=wrong", ts.URL, chanID, v-1), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { desc: "read page with boolean value", url: fmt.Sprintf("%s/channels/%s/messages?vb=true", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(boolMsgs)), @@ -667,13 +667,13 @@ func TestReadAll(t *testing.T) { { desc: "read page with non-boolean value", url: fmt.Sprintf("%s/channels/%s/messages?vb=yes", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { desc: "read page with string value", url: fmt.Sprintf("%s/channels/%s/messages?vs=%s", ts.URL, chanID, vs), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(stringMsgs)), @@ -683,7 +683,7 @@ func TestReadAll(t *testing.T) { { desc: "read page with data value", url: fmt.Sprintf("%s/channels/%s/messages?vd=%s", ts.URL, chanID, vd), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(dataMsgs)), @@ -693,20 +693,20 @@ func TestReadAll(t *testing.T) { { desc: "read page with non-float from", url: fmt.Sprintf("%s/channels/%s/messages?from=ABCD", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { desc: "read page with non-float to", url: fmt.Sprintf("%s/channels/%s/messages?to=ABCD", ts.URL, chanID), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { desc: "read page with from/to", url: fmt.Sprintf("%s/channels/%s/messages?from=%f&to=%f", ts.URL, chanID, messages[19].Time, messages[4].Time), - token: userToken, + token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, res: pageRes{ Total: uint64(len(messages[5:20])), diff --git a/readers/api/transport.go b/readers/api/transport.go index fc2bb5685d..c29c401c8f 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -6,8 +6,10 @@ package api import ( "context" "encoding/json" + "fmt" "net/http" "strconv" + "strings" kithttp "github.com/go-kit/kit/transport/http" "github.com/go-zoo/bone" @@ -38,17 +40,24 @@ const ( defLimit = 10 defOffset = 0 defFormat = "messages" + thingToken = "Thing " + userToken = "Bearer " ) var ( - errUnauthorizedAccess = errors.New("missing or invalid credentials provided") - errTokenNotBearer = errors.New("authentication scheme must be Bearer") - auth readers.Auth + errUnauthorizedAccess = errors.New("missing or invalid credentials provided") + errCannotAuthorizeUser = errors.New("authorization failed") + errThingAccess = errors.New("thing has no permission") + errUserAccess = errors.New("user has no permission") + errWrongToken = errors.New("incorrect or missing Authorization header") + thingsAuth mainflux.ThingsServiceClient + usersAuth mainflux.AuthServiceClient ) // MakeHandler returns a HTTP handler for API endpoints. -func MakeHandler(svc readers.MessageRepository, tc readers.Auth, svcName string) http.Handler { - auth = tc +func MakeHandler(svc readers.MessageRepository, tc mainflux.ThingsServiceClient, uc mainflux.AuthServiceClient, svcName string) http.Handler { + thingsAuth = tc + usersAuth = uc opts := []kithttp.ServerOption{ kithttp.ServerErrorEncoder(encodeError), @@ -215,29 +224,36 @@ func authorize(ctx context.Context, r *http.Request, chanID string) (err error) if token == "" { return errors.ErrAuthentication } - - if _, err := auth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: chanID}); err == nil { + if strings.HasPrefix(token, thingToken) { + token = strings.ReplaceAll(token, thingToken, "") + if _, err := thingsAuth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: chanID}); err != nil { + return errors.Wrap(errUnauthorizedAccess, errThingAccess) + } return nil } - user, err := auth.Identify(ctx, &mainflux.Token{Value: token}) - if err != nil { - e, ok := status.FromError(err) - if ok && e.Code() == codes.PermissionDenied { - return errUnauthorizedAccess + if strings.HasPrefix(token, userToken) { + token = strings.ReplaceAll(token, userToken, "") + user, err := usersAuth.Identify(ctx, &mainflux.Token{Value: token}) + if err != nil { + e, ok := status.FromError(err) + if ok && e.Code() == codes.PermissionDenied { + return errUnauthorizedAccess + } + return errors.Wrap(errUnauthorizedAccess, errCannotAuthorizeUser) } - return errUnauthorizedAccess - } - - _, err = auth.IsChannelOwner(ctx, &mainflux.ChannelOwnerReq{Owner: user.Email, ChanID: chanID}) - if err != nil { - e, ok := status.FromError(err) - if ok && e.Code() == codes.PermissionDenied { - return errUnauthorizedAccess + _, err = thingsAuth.IsChannelOwner(ctx, &mainflux.ChannelOwnerReq{Owner: user.Email, ChanID: chanID}) + if err != nil { + e, ok := status.FromError(err) + if ok && e.Code() == codes.PermissionDenied { + return errors.Wrap(errUnauthorizedAccess, errUserAccess) + } + return err } - return err + return nil } - return nil + + return errors.Wrap(errUnauthorizedAccess, errWrongToken) } diff --git a/readers/auth.go b/readers/auth.go deleted file mode 100644 index 7aa442e309..0000000000 --- a/readers/auth.go +++ /dev/null @@ -1,44 +0,0 @@ -package readers - -import ( - "context" - - "github.com/golang/protobuf/ptypes/empty" - "github.com/mainflux/mainflux" - "google.golang.org/grpc" -) - -type authService struct { - ts mainflux.ThingsServiceClient - us mainflux.AuthServiceClient -} - -type Auth interface { - Identify(ctx context.Context, in *mainflux.Token, opts ...grpc.CallOption) (*mainflux.UserIdentity, error) - CanAccessByKey(ctx context.Context, in *mainflux.AccessByKeyReq, opts ...grpc.CallOption) (*mainflux.ThingID, error) - IsChannelOwner(ctx context.Context, in *mainflux.ChannelOwnerReq, opts ...grpc.CallOption) (*empty.Empty, error) - CanAccessByID(ctx context.Context, in *mainflux.AccessByIDReq, opts ...grpc.CallOption) (*empty.Empty, error) -} - -func NewAuthService(ts mainflux.ThingsServiceClient, auth mainflux.AuthServiceClient) Auth { - return &authService{ - ts: ts, - us: auth, - } -} - -func (as *authService) Identify(ctx context.Context, in *mainflux.Token, opts ...grpc.CallOption) (*mainflux.UserIdentity, error) { - return as.us.Identify(ctx, in, opts...) -} - -func (as *authService) CanAccessByKey(ctx context.Context, in *mainflux.AccessByKeyReq, opts ...grpc.CallOption) (*mainflux.ThingID, error) { - return as.ts.CanAccessByKey(ctx, in, opts...) -} - -func (as *authService) CanAccessByID(ctx context.Context, in *mainflux.AccessByIDReq, opts ...grpc.CallOption) (*empty.Empty, error) { - return as.ts.CanAccessByID(ctx, in, opts...) -} - -func (as *authService) IsChannelOwner(ctx context.Context, in *mainflux.ChannelOwnerReq, opts ...grpc.CallOption) (*empty.Empty, error) { - return as.ts.IsChannelOwner(ctx, in, opts...) -} From 2e677bb889e4187ae873aac34872c5953f9588e4 Mon Sep 17 00:00:00 2001 From: mteodor Date: Tue, 19 Oct 2021 17:51:57 +0200 Subject: [PATCH 12/39] update api doc Signed-off-by: mteodor --- api/openapi/readers.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/openapi/readers.yml b/api/openapi/readers.yml index fa6d3fb48f..c8f83da7c8 100644 --- a/api/openapi/readers.yml +++ b/api/openapi/readers.yml @@ -109,7 +109,9 @@ components: parameters: Authorization: name: Authorization - description: Thing access token. + description: Thing or User access token. + For thing access use "Authorization: Thing " + For user access use "Authorization: Bearer " in: header schema: type: string From de14e8cbdf4994f58f41039c6b2005e4bc13f88d Mon Sep 17 00:00:00 2001 From: mteodor Date: Tue, 19 Oct 2021 17:59:36 +0200 Subject: [PATCH 13/39] remove unused package Signed-off-by: mteodor --- readers/api/transport.go | 1 - 1 file changed, 1 deletion(-) diff --git a/readers/api/transport.go b/readers/api/transport.go index c29c401c8f..acdfe84863 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -6,7 +6,6 @@ package api import ( "context" "encoding/json" - "fmt" "net/http" "strconv" "strings" From 612c57b3f2b3fa91eace7b3e996182eff14b778b Mon Sep 17 00:00:00 2001 From: mteodor Date: Fri, 22 Oct 2021 12:18:26 +0200 Subject: [PATCH 14/39] some refactor of cases for authorization switch Signed-off-by: mteodor --- readers/api/transport.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/readers/api/transport.go b/readers/api/transport.go index acdfe84863..648000df04 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -223,16 +223,15 @@ func authorize(ctx context.Context, r *http.Request, chanID string) (err error) if token == "" { return errors.ErrAuthentication } - if strings.HasPrefix(token, thingToken) { - token = strings.ReplaceAll(token, thingToken, "") + switch { + case strings.HasPrefix(token, thingToken): + token = strings.TrimPrefix(token, thingToken) if _, err := thingsAuth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: chanID}); err != nil { return errors.Wrap(errUnauthorizedAccess, errThingAccess) } return nil - } - - if strings.HasPrefix(token, userToken) { - token = strings.ReplaceAll(token, userToken, "") + case strings.HasPrefix(token, userToken): + token = strings.TrimPrefix(token, userToken) user, err := usersAuth.Identify(ctx, &mainflux.Token{Value: token}) if err != nil { e, ok := status.FromError(err) @@ -250,10 +249,10 @@ func authorize(ctx context.Context, r *http.Request, chanID string) (err error) return err } return nil - } - - return errors.Wrap(errUnauthorizedAccess, errWrongToken) + default: + return errors.Wrap(errUnauthorizedAccess, errWrongToken) + } } func readBoolValueQuery(r *http.Request, key string) (bool, error) { From f401adc31dbeb66d32adeadd71a4c8089015c77f Mon Sep 17 00:00:00 2001 From: mteodor Date: Tue, 26 Oct 2021 11:55:08 +0200 Subject: [PATCH 15/39] correct description in openapi Signed-off-by: mteodor --- api/openapi/readers.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/api/openapi/readers.yml b/api/openapi/readers.yml index c8f83da7c8..72023463ff 100644 --- a/api/openapi/readers.yml +++ b/api/openapi/readers.yml @@ -109,9 +109,10 @@ components: parameters: Authorization: name: Authorization - description: Thing or User access token. - For thing access use "Authorization: Thing " - For user access use "Authorization: Bearer " + description: | + Thing or User access token: + * For thing access use "Authorization: Thing " + * For user access use "Authorization: Bearer " in: header schema: type: string From 8b2179785740c2b25d1f45083d64a68b2b721012 Mon Sep 17 00:00:00 2001 From: mteodor Date: Fri, 5 Nov 2021 12:24:35 +0100 Subject: [PATCH 16/39] fix endpoint test to match auth service change Signed-off-by: mteodor --- readers/api/endpoint_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/readers/api/endpoint_test.go b/readers/api/endpoint_test.go index c05a612b6a..00e339730f 100644 --- a/readers/api/endpoint_test.go +++ b/readers/api/endpoint_test.go @@ -126,8 +126,9 @@ func TestReadAll(t *testing.T) { } thSvc := mocks.NewThingsService(map[string]string{email: chanID}) - - usrSvc := authmocks.NewAuthService(map[string]string{userToken: email}) + mockAuthzDB := map[string][]authmocks.SubjectSet{} + mockAuthzDB[email] = append(mockAuthzDB[email], authmocks.SubjectSet{Object: "authorities", Relation: "member"}) + usrSvc := authmocks.NewAuthService(map[string]string{userToken: email}, mockAuthzDB) repo := mocks.NewMessageRepository(chanID, fromSenml(messages)) ts := newServer(repo, thSvc, usrSvc) From f5be079d707cb7fde48a9494bb090069f265e4e2 Mon Sep 17 00:00:00 2001 From: mteodor Date: Tue, 23 Nov 2021 13:24:44 +0100 Subject: [PATCH 17/39] some rename Signed-off-by: mteodor --- readers/api/transport.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readers/api/transport.go b/readers/api/transport.go index 648000df04..30d0bf9b06 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -54,9 +54,9 @@ var ( ) // MakeHandler returns a HTTP handler for API endpoints. -func MakeHandler(svc readers.MessageRepository, tc mainflux.ThingsServiceClient, uc mainflux.AuthServiceClient, svcName string) http.Handler { +func MakeHandler(svc readers.MessageRepository, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient, svcName string) http.Handler { thingsAuth = tc - usersAuth = uc + usersAuth = ac opts := []kithttp.ServerOption{ kithttp.ServerErrorEncoder(encodeError), From dff426363c17aa20be8c418d9a67dab816b016e1 Mon Sep 17 00:00:00 2001 From: mteodor Date: Mon, 13 Dec 2021 13:37:05 +0100 Subject: [PATCH 18/39] initialize auth url Signed-off-by: mteodor --- cmd/cassandra-reader/main.go | 11 ++++++++++- cmd/influxdb-reader/main.go | 2 +- cmd/mongodb-reader/main.go | 13 +++++++++++-- cmd/postgres-reader/main.go | 12 ++++++++++-- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/cmd/cassandra-reader/main.go b/cmd/cassandra-reader/main.go index 5955691e87..cb0818e906 100644 --- a/cmd/cassandra-reader/main.go +++ b/cmd/cassandra-reader/main.go @@ -47,8 +47,9 @@ const ( defServerCert = "" defServerKey = "" defJaegerURL = "" - defThingsAuthURL = "localhost:8181" + defThingsAuthURL = "localhost:8183" defThingsAuthTimeout = "1s" + defUsersAuthURL = "localhost:8181" defUsersAuthTimeout = "1s" envLogLevel = "MF_CASSANDRA_READER_LOG_LEVEL" @@ -65,6 +66,7 @@ const ( envJaegerURL = "MF_JAEGER_URL" envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL" envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT" + envUsersAuthURL = "MF_AUTH_GRPC_URL" envUsersAuthTimeout = "MF_AUTH_GRPC_TIMEOUT" ) @@ -175,6 +177,11 @@ func loadConfig() config { log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error()) } + usersAuthTimeout, err := time.ParseDuration(mainflux.Env(envUsersAuthTimeout, defUsersAuthTimeout)) + if err != nil { + log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error()) + } + return config{ logLevel: mainflux.Env(envLogLevel, defLogLevel), port: mainflux.Env(envPort, defPort), @@ -185,6 +192,8 @@ func loadConfig() config { serverKey: mainflux.Env(envServerKey, defServerKey), jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL), thingsAuthURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL), + usersAuthURL: mainflux.Env(envUsersAuthURL, defUsersAuthURL), + usersAuthTimeout: usersAuthTimeout, thingsAuthTimeout: authTimeout, } } diff --git a/cmd/influxdb-reader/main.go b/cmd/influxdb-reader/main.go index 610c83db31..ea6f97790d 100644 --- a/cmd/influxdb-reader/main.go +++ b/cmd/influxdb-reader/main.go @@ -42,8 +42,8 @@ const ( defServerKey = "" defJaegerURL = "" defThingsAuthURL = "localhost:8183" - defUsersAuthURL = "localhost:8181" defThingsAuthTimeout = "1s" + defUsersAuthURL = "localhost:8181" defUsersAuthTimeout = "1s" envLogLevel = "MF_INFLUX_READER_LOG_LEVEL" diff --git a/cmd/mongodb-reader/main.go b/cmd/mongodb-reader/main.go index 44befa5a43..29c60b1d53 100644 --- a/cmd/mongodb-reader/main.go +++ b/cmd/mongodb-reader/main.go @@ -44,9 +44,9 @@ const ( defServerCert = "" defServerKey = "" defJaegerURL = "" - defThingsAuthURL = "localhost:8181" - defUsersAuthURL = "localhost:8181" + defThingsAuthURL = "localhost:8183" defThingsAuthTimeout = "1s" + defUsersAuthURL = "localhost:8181" defUsersAuthTimeout = "1s" envLogLevel = "MF_MONGO_READER_LOG_LEVEL" @@ -61,6 +61,8 @@ const ( envJaegerURL = "MF_JAEGER_URL" envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL" envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT" + envUsersAuthURL = "MF_AUTH_GRPC_URL" + envUsersAuthTimeout = "MF_AUTH_GRPC_TIMEOUT" ) type config struct { @@ -157,6 +159,11 @@ func loadConfigs() config { log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error()) } + usersAuthTimeout, err := time.ParseDuration(mainflux.Env(envUsersAuthTimeout, defUsersAuthTimeout)) + if err != nil { + log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error()) + } + return config{ logLevel: mainflux.Env(envLogLevel, defLogLevel), port: mainflux.Env(envPort, defPort), @@ -169,7 +176,9 @@ func loadConfigs() config { serverKey: mainflux.Env(envServerKey, defServerKey), jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL), thingsAuthURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL), + usersAuthURL: mainflux.Env(envUsersAuthURL, defUsersAuthURL), thingsAuthTimeout: authTimeout, + usersAuthTimeout: usersAuthTimeout, } } diff --git a/cmd/postgres-reader/main.go b/cmd/postgres-reader/main.go index 1c9b9cfb32..9299d0ddb0 100644 --- a/cmd/postgres-reader/main.go +++ b/cmd/postgres-reader/main.go @@ -49,8 +49,9 @@ const ( defDBSSLKey = "" defDBSSLRootCert = "" defJaegerURL = "" - defThingsAuthURL = "localhost:8181" + defThingsAuthURL = "localhost:8183" defThingsAuthTimeout = "1s" + defUsersAuthURL = "localhost:8181" defUsersAuthTimeout = "1s" envLogLevel = "MF_POSTGRES_READER_LOG_LEVEL" @@ -69,7 +70,7 @@ const ( envJaegerURL = "MF_JAEGER_URL" envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL" envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT" - envAuthURL = "MF_AUTH_GRPC_URL" + envUsersAuthURL = "MF_AUTH_GRPC_URL" envUsersAuthTimeout = "MF_AUTH_GRPC_TIMEOUT" ) @@ -178,6 +179,11 @@ func loadConfig() config { log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error()) } + usersAuthTimeout, err := time.ParseDuration(mainflux.Env(envUsersAuthTimeout, defUsersAuthTimeout)) + if err != nil { + log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error()) + } + return config{ logLevel: mainflux.Env(envLogLevel, defLogLevel), port: mainflux.Env(envPort, defPort), @@ -186,7 +192,9 @@ func loadConfig() config { dbConfig: dbConfig, jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL), thingsAuthURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL), + usersAuthURL: mainflux.Env(envUsersAuthURL, defUsersAuthURL), thingsAuthTimeout: authTimeout, + usersAuthTimeout: usersAuthTimeout, } } From c7976c2d327cb6cdf6b008effa82162b1e897ae2 Mon Sep 17 00:00:00 2001 From: mteodor Date: Tue, 21 Dec 2021 17:30:59 +0100 Subject: [PATCH 19/39] add env variables for auth service Signed-off-by: mteodor --- docker/addons/cassandra-reader/docker-compose.yml | 2 ++ docker/addons/influxdb-reader/docker-compose.yml | 2 ++ docker/addons/mongodb-reader/docker-compose.yml | 2 ++ docker/addons/postgres-reader/docker-compose.yml | 2 ++ 4 files changed, 8 insertions(+) diff --git a/docker/addons/cassandra-reader/docker-compose.yml b/docker/addons/cassandra-reader/docker-compose.yml index eeea67b015..cdd62047c9 100644 --- a/docker/addons/cassandra-reader/docker-compose.yml +++ b/docker/addons/cassandra-reader/docker-compose.yml @@ -27,6 +27,8 @@ services: MF_JAEGER_URL: ${MF_JAEGER_URL} MF_THINGS_AUTH_GRPC_URL: ${MF_THINGS_AUTH_GRPC_URL} MF_THINGS_AUTH_GRPC_TIMEOUT: ${MF_THINGS_AUTH_GRPC_TIMEOUT} + MF_AUTH_GRPC_URL: ${MF_AUTH_GRPC_URL} + MF_AUTH_GRPC_TIMEOUT : ${MF_AUTH_GRPC_TIMEOUT} ports: - ${MF_CASSANDRA_READER_PORT}:${MF_CASSANDRA_READER_PORT} networks: diff --git a/docker/addons/influxdb-reader/docker-compose.yml b/docker/addons/influxdb-reader/docker-compose.yml index 25ccd6fffb..d7fde6809d 100644 --- a/docker/addons/influxdb-reader/docker-compose.yml +++ b/docker/addons/influxdb-reader/docker-compose.yml @@ -32,6 +32,8 @@ services: MF_JAEGER_URL: ${MF_JAEGER_URL} MF_THINGS_AUTH_GRPC_URL: ${MF_THINGS_AUTH_GRPC_URL} MF_THINGS_AUTH_GRPC_TIMEOUT: ${MF_THINGS_AUTH_GRPC_TIMEOUT} + MF_AUTH_GRPC_URL: ${MF_AUTH_GRPC_URL} + MF_AUTH_GRPC_TIMEOUT : ${MF_AUTH_GRPC_TIMEOUT} ports: - ${MF_INFLUX_READER_PORT}:${MF_INFLUX_READER_PORT} networks: diff --git a/docker/addons/mongodb-reader/docker-compose.yml b/docker/addons/mongodb-reader/docker-compose.yml index ec14081240..7f66f077d3 100644 --- a/docker/addons/mongodb-reader/docker-compose.yml +++ b/docker/addons/mongodb-reader/docker-compose.yml @@ -29,6 +29,8 @@ services: MF_JAEGER_URL: ${MF_JAEGER_URL} MF_THINGS_AUTH_GRPC_URL: ${MF_THINGS_AUTH_GRPC_URL} MF_THINGS_AUTH_GRPC_TIMEOUT: ${MF_THINGS_AUTH_GRPC_TIMEOUT} + MF_AUTH_GRPC_URL: ${MF_AUTH_GRPC_URL} + MF_AUTH_GRPC_TIMEOUT : ${MF_AUTH_GRPC_TIMEOUT} ports: - ${MF_MONGO_READER_PORT}:${MF_MONGO_READER_PORT} networks: diff --git a/docker/addons/postgres-reader/docker-compose.yml b/docker/addons/postgres-reader/docker-compose.yml index b4a2f52123..67cc87f8ce 100644 --- a/docker/addons/postgres-reader/docker-compose.yml +++ b/docker/addons/postgres-reader/docker-compose.yml @@ -35,6 +35,8 @@ services: MF_JAEGER_URL: ${MF_JAEGER_URL} MF_THINGS_AUTH_GRPC_URL: ${MF_THINGS_AUTH_GRPC_URL} MF_THINGS_AUTH_GRPC_TIMEOUT: ${MF_THINGS_AUTH_GRPC_TIMEOUT} + MF_AUTH_GRPC_URL: ${MF_AUTH_GRPC_URL} + MF_AUTH_GRPC_TIMEOUT : ${MF_AUTH_GRPC_TIMEOUT} ports: - ${MF_POSTGRES_READER_PORT}:${MF_POSTGRES_READER_PORT} networks: From 9dafb25b47220afcf942b847eb1d51f7d90f98eb Mon Sep 17 00:00:00 2001 From: mteodor Date: Wed, 29 Dec 2021 10:52:55 +0100 Subject: [PATCH 20/39] fix spelling Signed-off-by: mteodor --- readers/api/endpoint_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readers/api/endpoint_test.go b/readers/api/endpoint_test.go index 00e339730f..9151c96acc 100644 --- a/readers/api/endpoint_test.go +++ b/readers/api/endpoint_test.go @@ -243,7 +243,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with senml fornat", + desc: "read page with senml format", url: fmt.Sprintf("%s/channels/%s/messages?format=messages", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -524,7 +524,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with senml fornat", + desc: "read page with senml format", url: fmt.Sprintf("%s/channels/%s/messages?format=messages", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, From 31ed39e5c5ba37e9146e474fb0bf0ce64703e16d Mon Sep 17 00:00:00 2001 From: mteodor Date: Thu, 30 Dec 2021 10:13:51 +0100 Subject: [PATCH 21/39] Things prefix and no prefix for Thing authorization, Bearer for user Signed-off-by: mteodor --- readers/api/transport.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/readers/api/transport.go b/readers/api/transport.go index 30d0bf9b06..a16b651527 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -224,12 +224,6 @@ func authorize(ctx context.Context, r *http.Request, chanID string) (err error) return errors.ErrAuthentication } switch { - case strings.HasPrefix(token, thingToken): - token = strings.TrimPrefix(token, thingToken) - if _, err := thingsAuth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: chanID}); err != nil { - return errors.Wrap(errUnauthorizedAccess, errThingAccess) - } - return nil case strings.HasPrefix(token, userToken): token = strings.TrimPrefix(token, userToken) user, err := usersAuth.Identify(ctx, &mainflux.Token{Value: token}) @@ -250,8 +244,11 @@ func authorize(ctx context.Context, r *http.Request, chanID string) (err error) } return nil default: - return errors.Wrap(errUnauthorizedAccess, errWrongToken) - + token = strings.TrimPrefix(token, thingToken) + if _, err := thingsAuth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: chanID}); err != nil { + return errors.Wrap(errUnauthorizedAccess, errThingAccess) + } + return nil } } From 5e1349268e8fe12b4d6dff5fa3753549674be2c7 Mon Sep 17 00:00:00 2001 From: mteodor Date: Fri, 14 Jan 2022 16:53:57 +0100 Subject: [PATCH 22/39] update readme file Signed-off-by: mteodor --- readers/cassandra/README.md | 2 ++ readers/influxdb/README.md | 3 +++ readers/mongodb/README.md | 3 +++ readers/postgres/README.md | 38 +++++++++++++++++++------------------ 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/readers/cassandra/README.md b/readers/cassandra/README.md index 4a865d6b29..d5fb9b5324 100644 --- a/readers/cassandra/README.md +++ b/readers/cassandra/README.md @@ -23,6 +23,8 @@ default values. | MF_JAEGER_URL | Jaeger server URL | localhost:6831 | | MF_THINGS_AUTH_GRPC_URL | Things service Auth gRPC URL | localhost:8181 | | MF_THINGS_AUTH_GRPC_TIMEOUT | Things service Auth gRPC request timeout in seconds | 1 | +| MF_AUTH_GRPC_URL | Auth service gRPC URL | localhost:8181 | +| MF_AUTH_GRPC_TIMEOUT | Auth service gRPC request timeout in seconds | 1s | ## Deployment diff --git a/readers/influxdb/README.md b/readers/influxdb/README.md index 89d76f159e..d92956673c 100644 --- a/readers/influxdb/README.md +++ b/readers/influxdb/README.md @@ -23,6 +23,9 @@ default values. | MF_JAEGER_URL | Jaeger server URL | localhost:6831 | | MF_THINGS_AUTH_GRPC_URL | Things service Auth gRPC URL | localhost:8181 | | MF_THINGS_AUTH_GRPC_TIMEOUT | Things service Auth gRPC request timeout in seconds | 1s | +| MF_AUTH_GRPC_URL | Auth service gRPC URL | localhost:8181 | +| MF_AUTH_GRPC_TIMEOUT | Auth service gRPC request timeout in seconds | 1s | + ## Deployment diff --git a/readers/mongodb/README.md b/readers/mongodb/README.md index b86c126f50..722bc9bddf 100644 --- a/readers/mongodb/README.md +++ b/readers/mongodb/README.md @@ -21,6 +21,9 @@ default values. | MF_JAEGER_URL | Jaeger server URL | localhost:6831 | | MF_THINGS_AUTH_GRPC_URL | Things service Auth gRPC URL | localhost:8181 | | MF_THINGS_AUTH_GRPC_TIMEOUT | Things service Auth gRPC request timeout in seconds | 1s | +| MF_AUTH_GRPC_URL | Auth service gRPC URL | localhost:8181 | +| MF_AUTH_GRPC_TIMEOUT | Auth service gRPC request timeout in seconds | 1s | + ## Deployment diff --git a/readers/postgres/README.md b/readers/postgres/README.md index ad27e5145e..9a0214e1a8 100644 --- a/readers/postgres/README.md +++ b/readers/postgres/README.md @@ -8,24 +8,26 @@ The service is configured using the environment variables presented in the following table. Note that any unset variables will be replaced with their default values. -| Variable | Description | Default | -|-------------------------------------|---------------------------------------------|----------------| -| MF_POSTGRES_READER_LOG_LEVEL | Service log level | debug | -| MF_POSTGRES_READER_PORT | Service HTTP port | 8180 | -| MF_POSTGRES_READER_CLIENT_TLS | TLS mode flag | false | -| MF_POSTGRES_READER_CA_CERTS | Path to trusted CAs in PEM format | | -| MF_POSTGRES_READER_DB_HOST | Postgres DB host | postgres | -| MF_POSTGRES_READER_DB_PORT | Postgres DB port | 5432 | -| MF_POSTGRES_READER_DB_USER | Postgres user | mainflux | -| MF_POSTGRES_READER_DB_PASS | Postgres password | mainflux | -| MF_POSTGRES_READER_DB | Postgres database name | messages | -| MF_POSTGRES_READER_DB_SSL_MODE | Postgres SSL mode | disabled | -| MF_POSTGRES_READER_DB_SSL_CERT | Postgres SSL certificate path | "" | -| MF_POSTGRES_READER_DB_SSL_KEY | Postgres SSL key | "" | -| MF_POSTGRES_READER_DB_SSL_ROOT_CERT | Postgres SSL root certificate path | "" | -| MF_JAEGER_URL | Jaeger server URL | localhost:6831 | -| MF_THINGS_AUTH_GRPC_URL | Things service Auth gRPC URL | localhost:8181 | -| MF_THINGS_AUTH_GRPC_TIMEOUT | Things service Auth gRPC timeout in seconds | 1s | +| Variable | Description | Default | +|-------------------------------------|----------------------------------------------|----------------| +| MF_POSTGRES_READER_LOG_LEVEL | Service log level | debug | +| MF_POSTGRES_READER_PORT | Service HTTP port | 8180 | +| MF_POSTGRES_READER_CLIENT_TLS | TLS mode flag | false | +| MF_POSTGRES_READER_CA_CERTS | Path to trusted CAs in PEM format | | +| MF_POSTGRES_READER_DB_HOST | Postgres DB host | postgres | +| MF_POSTGRES_READER_DB_PORT | Postgres DB port | 5432 | +| MF_POSTGRES_READER_DB_USER | Postgres user | mainflux | +| MF_POSTGRES_READER_DB_PASS | Postgres password | mainflux | +| MF_POSTGRES_READER_DB | Postgres database name | messages | +| MF_POSTGRES_READER_DB_SSL_MODE | Postgres SSL mode | disabled | +| MF_POSTGRES_READER_DB_SSL_CERT | Postgres SSL certificate path | "" | +| MF_POSTGRES_READER_DB_SSL_KEY | Postgres SSL key | "" | +| MF_POSTGRES_READER_DB_SSL_ROOT_CERT | Postgres SSL root certificate path | "" | +| MF_JAEGER_URL | Jaeger server URL | localhost:6831 | +| MF_THINGS_AUTH_GRPC_URL | Things service Auth gRPC URL | localhost:8181 | +| MF_THINGS_AUTH_GRPC_TIMEOUT | Things service Auth gRPC timeout in seconds | 1s | +| MF_AUTH_GRPC_URL | Auth service gRPC URL | localhost:8181 | +| MF_AUTH_GRPC_TIMEOUT | Auth service gRPC request timeout in seconds | 1s | ## Deployment From a2fd9e884d3e59115193901f2c5792a59e250842 Mon Sep 17 00:00:00 2001 From: mteodor Date: Fri, 14 Jan 2022 18:33:25 +0100 Subject: [PATCH 23/39] fix default things grpc port Signed-off-by: mteodor --- bootstrap/service_test.go | 4 ++++ cmd/coap/main.go | 2 +- cmd/http/main.go | 2 +- cmd/mqtt/main.go | 2 +- readers/cassandra/README.md | 2 +- readers/influxdb/README.md | 2 +- readers/mongodb/README.md | 2 +- readers/postgres/README.md | 2 +- 8 files changed, 11 insertions(+), 7 deletions(-) diff --git a/bootstrap/service_test.go b/bootstrap/service_test.go index 83fd98645e..4db795fd95 100644 --- a/bootstrap/service_test.go +++ b/bootstrap/service_test.go @@ -9,6 +9,7 @@ import ( "crypto/cipher" "crypto/rand" "encoding/hex" + "encoding/json" "fmt" "io" "net/http/httptest" @@ -60,6 +61,9 @@ func newService(auth mainflux.AuthServiceClient, url string) bootstrap.Service { ThingsURL: url, } + t := map[string]interface{}{} + t["test"]= "test" + json.Marshal(t) sdk := mfsdk.NewSDK(config) return bootstrap.New(auth, things, sdk, encKey) } diff --git a/cmd/coap/main.go b/cmd/coap/main.go index 52ba76d64a..cac5bc3cd8 100644 --- a/cmd/coap/main.go +++ b/cmd/coap/main.go @@ -37,7 +37,7 @@ const ( defClientTLS = "false" defCACerts = "" defJaegerURL = "" - defThingsAuthURL = "localhost:8181" + defThingsAuthURL = "localhost:8183" defThingsAuthTimeout = "1s" envPort = "MF_COAP_ADAPTER_PORT" diff --git a/cmd/http/main.go b/cmd/http/main.go index 7e2d8607a0..252d02e32f 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -37,7 +37,7 @@ const ( defPort = "8180" defNatsURL = "nats://localhost:4222" defJaegerURL = "" - defThingsAuthURL = "localhost:8181" + defThingsAuthURL = "localhost:8183" defThingsAuthTimeout = "1s" envLogLevel = "MF_HTTP_ADAPTER_LOG_LEVEL" diff --git a/cmd/mqtt/main.go b/cmd/mqtt/main.go index a576989295..ca597aea63 100644 --- a/cmd/mqtt/main.go +++ b/cmd/mqtt/main.go @@ -58,7 +58,7 @@ const ( envHTTPTargetPort = "MF_MQTT_ADAPTER_WS_TARGET_PORT" envHTTPTargetPath = "MF_MQTT_ADAPTER_WS_TARGET_PATH" // Things - defThingsAuthURL = "localhost:8181" + defThingsAuthURL = "localhost:8183" defThingsAuthTimeout = "1s" envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL" envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT" diff --git a/readers/cassandra/README.md b/readers/cassandra/README.md index d5fb9b5324..1d2ef40c0d 100644 --- a/readers/cassandra/README.md +++ b/readers/cassandra/README.md @@ -21,7 +21,7 @@ default values. | MF_CASSANDRA_READER_SERVER_CERT | Path to server certificate in pem format | | | MF_CASSANDRA_READER_SERVER_KEY | Path to server key in pem format | | | MF_JAEGER_URL | Jaeger server URL | localhost:6831 | -| MF_THINGS_AUTH_GRPC_URL | Things service Auth gRPC URL | localhost:8181 | +| MF_THINGS_AUTH_GRPC_URL | Things service Auth gRPC URL | localhost:8183 | | MF_THINGS_AUTH_GRPC_TIMEOUT | Things service Auth gRPC request timeout in seconds | 1 | | MF_AUTH_GRPC_URL | Auth service gRPC URL | localhost:8181 | | MF_AUTH_GRPC_TIMEOUT | Auth service gRPC request timeout in seconds | 1s | diff --git a/readers/influxdb/README.md b/readers/influxdb/README.md index d92956673c..c1199ecc6f 100644 --- a/readers/influxdb/README.md +++ b/readers/influxdb/README.md @@ -21,7 +21,7 @@ default values. | MF_INFLUX_READER_SERVER_CERT | Path to server certificate in pem format | | | MF_INFLUX_READER_SERVER_KEY | Path to server key in pem format | | | MF_JAEGER_URL | Jaeger server URL | localhost:6831 | -| MF_THINGS_AUTH_GRPC_URL | Things service Auth gRPC URL | localhost:8181 | +| MF_THINGS_AUTH_GRPC_URL | Things service Auth gRPC URL | localhost:8183 | | MF_THINGS_AUTH_GRPC_TIMEOUT | Things service Auth gRPC request timeout in seconds | 1s | | MF_AUTH_GRPC_URL | Auth service gRPC URL | localhost:8181 | | MF_AUTH_GRPC_TIMEOUT | Auth service gRPC request timeout in seconds | 1s | diff --git a/readers/mongodb/README.md b/readers/mongodb/README.md index 722bc9bddf..0d6826a6b8 100644 --- a/readers/mongodb/README.md +++ b/readers/mongodb/README.md @@ -19,7 +19,7 @@ default values. | MF_MONGO_SERVER_CERT | Path to server certificate in pem format | | | MF_MONGO_SERVER_KEY | Path to server key in pem format | | | MF_JAEGER_URL | Jaeger server URL | localhost:6831 | -| MF_THINGS_AUTH_GRPC_URL | Things service Auth gRPC URL | localhost:8181 | +| MF_THINGS_AUTH_GRPC_URL | Things service Auth gRPC URL | localhost:8183 | | MF_THINGS_AUTH_GRPC_TIMEOUT | Things service Auth gRPC request timeout in seconds | 1s | | MF_AUTH_GRPC_URL | Auth service gRPC URL | localhost:8181 | | MF_AUTH_GRPC_TIMEOUT | Auth service gRPC request timeout in seconds | 1s | diff --git a/readers/postgres/README.md b/readers/postgres/README.md index 9a0214e1a8..037ef1b148 100644 --- a/readers/postgres/README.md +++ b/readers/postgres/README.md @@ -24,7 +24,7 @@ default values. | MF_POSTGRES_READER_DB_SSL_KEY | Postgres SSL key | "" | | MF_POSTGRES_READER_DB_SSL_ROOT_CERT | Postgres SSL root certificate path | "" | | MF_JAEGER_URL | Jaeger server URL | localhost:6831 | -| MF_THINGS_AUTH_GRPC_URL | Things service Auth gRPC URL | localhost:8181 | +| MF_THINGS_AUTH_GRPC_URL | Things service Auth gRPC URL | localhost:8183 | | MF_THINGS_AUTH_GRPC_TIMEOUT | Things service Auth gRPC timeout in seconds | 1s | | MF_AUTH_GRPC_URL | Auth service gRPC URL | localhost:8181 | | MF_AUTH_GRPC_TIMEOUT | Auth service gRPC request timeout in seconds | 1s | From 3f45c5ecf67d3622719bbeba6537c50b076c59f7 Mon Sep 17 00:00:00 2001 From: mteodor Date: Thu, 27 Jan 2022 13:18:59 +0100 Subject: [PATCH 24/39] enable user reading for timescaledb Signed-off-by: mteodor --- cmd/timescale-reader/main.go | 42 +++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/cmd/timescale-reader/main.go b/cmd/timescale-reader/main.go index dbe792c76f..5f9b5bd3c0 100644 --- a/cmd/timescale-reader/main.go +++ b/cmd/timescale-reader/main.go @@ -18,6 +18,7 @@ import ( kitprometheus "github.com/go-kit/kit/metrics/prometheus" "github.com/jmoiron/sqlx" "github.com/mainflux/mainflux" + authapi "github.com/mainflux/mainflux/auth/api/grpc" "github.com/mainflux/mainflux/logger" "github.com/mainflux/mainflux/readers" "github.com/mainflux/mainflux/readers/api" @@ -76,7 +77,9 @@ type config struct { dbConfig timescale.Config jaegerURL string thingsAuthURL string + usersAuthURL string thingsAuthTimeout time.Duration + usersAuthTimeout time.Duration } func main() { @@ -93,6 +96,13 @@ func main() { thingsTracer, thingsCloser := initJaeger("things", cfg.jaegerURL, logger) defer thingsCloser.Close() + authTracer, authCloser := initJaeger("auth", cfg.jaegerURL, logger) + defer authCloser.Close() + + authConn := connectToAuth(cfg, logger) + defer authConn.Close() + auth := authapi.NewClient(authTracer, authConn, cfg.usersAuthTimeout) + tc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsAuthTimeout) db := connectToDB(cfg.dbConfig, logger) @@ -102,7 +112,7 @@ func main() { errs := make(chan error, 2) - go startHTTPServer(repo, tc, cfg.port, logger, errs) + go startHTTPServer(repo, tc, auth, cfg.port, logger, errs) go func() { c := make(chan os.Signal) @@ -149,6 +159,32 @@ func loadConfig() config { } } +func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn { + var opts []grpc.DialOption + logger.Info("Connecting to auth via gRPC") + if cfg.clientTLS { + if cfg.caCerts != "" { + tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "") + if err != nil { + logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err)) + os.Exit(1) + } + opts = append(opts, grpc.WithTransportCredentials(tpc)) + } + } else { + opts = append(opts, grpc.WithInsecure()) + logger.Info("gRPC communication is not encrypted") + } + + conn, err := grpc.Dial(cfg.usersAuthURL, opts...) + if err != nil { + logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err)) + os.Exit(1) + } + logger.Info(fmt.Sprintf("Established gRPC connection to auth via gRPC: %s", cfg.usersAuthURL)) + return conn +} + func connectToDB(dbConfig timescale.Config, logger logger.Logger) *sqlx.DB { db, err := timescale.Connect(dbConfig) if err != nil { @@ -228,8 +264,8 @@ func newService(db *sqlx.DB, logger logger.Logger) readers.MessageRepository { return svc } -func startHTTPServer(repo readers.MessageRepository, tc mainflux.ThingsServiceClient, port string, logger logger.Logger, errs chan error) { +func startHTTPServer(repo readers.MessageRepository, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient, port string, logger logger.Logger, errs chan error) { p := fmt.Sprintf(":%s", port) logger.Info(fmt.Sprintf("Timescale reader service started, exposed port %s", port)) - errs <- http.ListenAndServe(p, api.MakeHandler(repo, tc, svcName)) + errs <- http.ListenAndServe(p, api.MakeHandler(repo, tc, ac, svcName)) } From 39c94b7d9fcf3767f2a0eb7e87249071baf51201 Mon Sep 17 00:00:00 2001 From: mteodor Date: Fri, 28 Jan 2022 12:10:41 +0100 Subject: [PATCH 25/39] remove not used error Signed-off-by: mteodor --- readers/api/transport.go | 1 - 1 file changed, 1 deletion(-) diff --git a/readers/api/transport.go b/readers/api/transport.go index a16b651527..c9f3b03069 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -48,7 +48,6 @@ var ( errCannotAuthorizeUser = errors.New("authorization failed") errThingAccess = errors.New("thing has no permission") errUserAccess = errors.New("user has no permission") - errWrongToken = errors.New("incorrect or missing Authorization header") thingsAuth mainflux.ThingsServiceClient usersAuth mainflux.AuthServiceClient ) From 427d276eebfb9493511ade44f7d155259add782f Mon Sep 17 00:00:00 2001 From: mteodor Date: Fri, 28 Jan 2022 18:26:09 +0100 Subject: [PATCH 26/39] improve errors Signed-off-by: mteodor --- readers/api/transport.go | 14 +++++++------- readers/mocks/things.go | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/readers/api/transport.go b/readers/api/transport.go index c9f3b03069..a012d76dfe 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -44,7 +44,7 @@ const ( ) var ( - errUnauthorizedAccess = errors.New("missing or invalid credentials provided") + errEmptyToken = errors.New("missing token") errCannotAuthorizeUser = errors.New("authorization failed") errThingAccess = errors.New("thing has no permission") errUserAccess = errors.New("user has no permission") @@ -82,7 +82,7 @@ func decodeList(ctx context.Context, r *http.Request) (interface{}, error) { } if err := authorize(ctx, r, chanID); err != nil { - return nil, err + return nil, errors.ErrAuthorization } offset, err := httputil.ReadUintQuery(r, offsetKey, defOffset) @@ -220,7 +220,7 @@ func encodeError(_ context.Context, err error, w http.ResponseWriter) { func authorize(ctx context.Context, r *http.Request, chanID string) (err error) { token := r.Header.Get("Authorization") if token == "" { - return errors.ErrAuthentication + return errEmptyToken } switch { case strings.HasPrefix(token, userToken): @@ -229,15 +229,15 @@ func authorize(ctx context.Context, r *http.Request, chanID string) (err error) if err != nil { e, ok := status.FromError(err) if ok && e.Code() == codes.PermissionDenied { - return errUnauthorizedAccess + return errCannotAuthorizeUser } - return errors.Wrap(errUnauthorizedAccess, errCannotAuthorizeUser) + return errCannotAuthorizeUser } _, err = thingsAuth.IsChannelOwner(ctx, &mainflux.ChannelOwnerReq{Owner: user.Email, ChanID: chanID}) if err != nil { e, ok := status.FromError(err) if ok && e.Code() == codes.PermissionDenied { - return errors.Wrap(errUnauthorizedAccess, errUserAccess) + return errors.Wrap(errCannotAuthorizeUser, err) } return err } @@ -245,7 +245,7 @@ func authorize(ctx context.Context, r *http.Request, chanID string) (err error) default: token = strings.TrimPrefix(token, thingToken) if _, err := thingsAuth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: chanID}); err != nil { - return errors.Wrap(errUnauthorizedAccess, errThingAccess) + return errors.Wrap(errThingAccess, err) } return nil } diff --git a/readers/mocks/things.go b/readers/mocks/things.go index e3c4636703..9cf0d3dc89 100644 --- a/readers/mocks/things.go +++ b/readers/mocks/things.go @@ -51,7 +51,7 @@ func (svc thingsServiceMock) IsChannelOwner(ctx context.Context, in *mainflux.Ch return nil, nil } } - return nil, users.ErrUnauthorizedAccess + return nil, errors.ErrUnauthorizedAccess } func (svc thingsServiceMock) Identify(context.Context, *mainflux.Token, ...grpc.CallOption) (*mainflux.ThingID, error) { From cf7b6abd7ee2fc6f4e53d166a5d5f0a729dc0b3d Mon Sep 17 00:00:00 2001 From: mteodor Date: Tue, 1 Feb 2022 14:43:45 +0100 Subject: [PATCH 27/39] refactor authorize Signed-off-by: mteodor --- readers/api/endpoint.go | 10 +++++++--- readers/api/requests.go | 4 ++++ readers/api/transport.go | 31 ++++++++++--------------------- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/readers/api/endpoint.go b/readers/api/endpoint.go index 865dddde2f..fb25327789 100644 --- a/readers/api/endpoint.go +++ b/readers/api/endpoint.go @@ -7,17 +7,21 @@ import ( "context" "github.com/go-kit/kit/endpoint" + "github.com/mainflux/mainflux" + "github.com/mainflux/mainflux/pkg/errors" "github.com/mainflux/mainflux/readers" ) -func listMessagesEndpoint(svc readers.MessageRepository) endpoint.Endpoint { - return func(_ context.Context, request interface{}) (interface{}, error) { +func listMessagesEndpoint(svc readers.MessageRepository, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(listMessagesReq) if err := req.validate(); err != nil { return nil, err } - + if err := authorize(ctx, req, tc, ac); err != nil { + return nil, errors.Wrap(errors.ErrAuthorization, err) + } page, err := svc.ReadAll(req.chanID, req.pageMeta) if err != nil { return nil, err diff --git a/readers/api/requests.go b/readers/api/requests.go index 86e7f1098e..8dab173ebf 100644 --- a/readers/api/requests.go +++ b/readers/api/requests.go @@ -14,10 +14,14 @@ type apiReq interface { type listMessagesReq struct { chanID string + token string pageMeta readers.PageMetadata } func (req listMessagesReq) validate() error { + if req.token == "" { + return errors.ErrAuthorization + } if req.pageMeta.Limit < 1 || req.pageMeta.Offset < 0 { return errors.ErrInvalidQueryParams } diff --git a/readers/api/transport.go b/readers/api/transport.go index a012d76dfe..e9b8caf09d 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -63,7 +63,7 @@ func MakeHandler(svc readers.MessageRepository, tc mainflux.ThingsServiceClient, mux := bone.New() mux.Get("/channels/:chanID/messages", kithttp.NewServer( - listMessagesEndpoint(svc), + listMessagesEndpoint(svc, tc, ac), decodeList, encodeResponse, opts..., @@ -76,15 +76,6 @@ func MakeHandler(svc readers.MessageRepository, tc mainflux.ThingsServiceClient, } func decodeList(ctx context.Context, r *http.Request) (interface{}, error) { - chanID := bone.GetValue(r, "chanID") - if chanID == "" { - return nil, errors.ErrInvalidQueryParams - } - - if err := authorize(ctx, r, chanID); err != nil { - return nil, errors.ErrAuthorization - } - offset, err := httputil.ReadUintQuery(r, offsetKey, defOffset) if err != nil { return nil, err @@ -151,7 +142,8 @@ func decodeList(ctx context.Context, r *http.Request) (interface{}, error) { } req := listMessagesReq{ - chanID: chanID, + chanID: bone.GetValue(r, "chanID"), + token: r.Header.Get("Authorization"), pageMeta: readers.PageMetadata{ Offset: offset, Limit: limit, @@ -217,14 +209,11 @@ func encodeError(_ context.Context, err error, w http.ResponseWriter) { } } -func authorize(ctx context.Context, r *http.Request, chanID string) (err error) { - token := r.Header.Get("Authorization") - if token == "" { - return errEmptyToken - } +func authorize(ctx context.Context, req listMessagesReq, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient) (err error) { + switch { - case strings.HasPrefix(token, userToken): - token = strings.TrimPrefix(token, userToken) + case strings.HasPrefix(req.token, userToken): + token := strings.TrimPrefix(req.token, userToken) user, err := usersAuth.Identify(ctx, &mainflux.Token{Value: token}) if err != nil { e, ok := status.FromError(err) @@ -233,7 +222,7 @@ func authorize(ctx context.Context, r *http.Request, chanID string) (err error) } return errCannotAuthorizeUser } - _, err = thingsAuth.IsChannelOwner(ctx, &mainflux.ChannelOwnerReq{Owner: user.Email, ChanID: chanID}) + _, err = thingsAuth.IsChannelOwner(ctx, &mainflux.ChannelOwnerReq{Owner: user.Email, ChanID: req.chanID}) if err != nil { e, ok := status.FromError(err) if ok && e.Code() == codes.PermissionDenied { @@ -243,8 +232,8 @@ func authorize(ctx context.Context, r *http.Request, chanID string) (err error) } return nil default: - token = strings.TrimPrefix(token, thingToken) - if _, err := thingsAuth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: chanID}); err != nil { + token := strings.TrimPrefix(req.token, thingToken) + if _, err := thingsAuth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: req.chanID}); err != nil { return errors.Wrap(errThingAccess, err) } return nil From fabdb8d5f63447370741ecfb885db53ccd88ee15 Mon Sep 17 00:00:00 2001 From: mteodor Date: Tue, 1 Feb 2022 18:07:00 +0100 Subject: [PATCH 28/39] add chanID check Signed-off-by: mteodor --- readers/api/requests.go | 4 ++-- readers/api/transport.go | 1 - readers/mocks/things.go | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/readers/api/requests.go b/readers/api/requests.go index 8dab173ebf..6b4b51e262 100644 --- a/readers/api/requests.go +++ b/readers/api/requests.go @@ -19,8 +19,8 @@ type listMessagesReq struct { } func (req listMessagesReq) validate() error { - if req.token == "" { - return errors.ErrAuthorization + if req.token == "" || req.chanID == "" { + return errors.ErrAuthentication } if req.pageMeta.Limit < 1 || req.pageMeta.Offset < 0 { return errors.ErrInvalidQueryParams diff --git a/readers/api/transport.go b/readers/api/transport.go index e9b8caf09d..d85ee9563b 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -210,7 +210,6 @@ func encodeError(_ context.Context, err error, w http.ResponseWriter) { } func authorize(ctx context.Context, req listMessagesReq, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient) (err error) { - switch { case strings.HasPrefix(req.token, userToken): token := strings.TrimPrefix(req.token, userToken) diff --git a/readers/mocks/things.go b/readers/mocks/things.go index 9cf0d3dc89..918dcca639 100644 --- a/readers/mocks/things.go +++ b/readers/mocks/things.go @@ -35,7 +35,7 @@ func (svc thingsServiceMock) CanAccessByKey(ctx context.Context, in *mainflux.Ac } if token == "token" { - return nil, errUnauthorized + return nil, errors.ErrAuthorization } return &mainflux.ThingID{Value: token}, nil @@ -51,7 +51,7 @@ func (svc thingsServiceMock) IsChannelOwner(ctx context.Context, in *mainflux.Ch return nil, nil } } - return nil, errors.ErrUnauthorizedAccess + return nil, errors.ErrAuthorization } func (svc thingsServiceMock) Identify(context.Context, *mainflux.Token, ...grpc.CallOption) (*mainflux.ThingID, error) { From 181d0cec68e18e1e2072648abc60ac0a93a843c3 Mon Sep 17 00:00:00 2001 From: mteodor Date: Wed, 2 Feb 2022 16:45:10 +0100 Subject: [PATCH 29/39] inline some error checking Signed-off-by: mteodor --- readers/api/transport.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/readers/api/transport.go b/readers/api/transport.go index d85ee9563b..5de8369614 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -213,16 +213,15 @@ func authorize(ctx context.Context, req listMessagesReq, tc mainflux.ThingsServi switch { case strings.HasPrefix(req.token, userToken): token := strings.TrimPrefix(req.token, userToken) - user, err := usersAuth.Identify(ctx, &mainflux.Token{Value: token}) - if err != nil { + var user *mainflux.UserIdentity + if user, err = usersAuth.Identify(ctx, &mainflux.Token{Value: token}); err != nil { e, ok := status.FromError(err) if ok && e.Code() == codes.PermissionDenied { return errCannotAuthorizeUser } return errCannotAuthorizeUser } - _, err = thingsAuth.IsChannelOwner(ctx, &mainflux.ChannelOwnerReq{Owner: user.Email, ChanID: req.chanID}) - if err != nil { + if _, err = thingsAuth.IsChannelOwner(ctx, &mainflux.ChannelOwnerReq{Owner: user.Email, ChanID: req.chanID}); err != nil { e, ok := status.FromError(err) if ok && e.Code() == codes.PermissionDenied { return errors.Wrap(errCannotAuthorizeUser, err) From aa5f3c14c39247232b480461bb20ed38481c4358 Mon Sep 17 00:00:00 2001 From: mteodor Date: Thu, 3 Feb 2022 11:36:50 +0100 Subject: [PATCH 30/39] fixing errors Signed-off-by: mteodor --- readers/api/requests.go | 5 ++++- readers/api/transport.go | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/readers/api/requests.go b/readers/api/requests.go index 6b4b51e262..65cb6508ba 100644 --- a/readers/api/requests.go +++ b/readers/api/requests.go @@ -19,9 +19,12 @@ type listMessagesReq struct { } func (req listMessagesReq) validate() error { - if req.token == "" || req.chanID == "" { + if req.token == "" { return errors.ErrAuthentication } + if req.chanID == "" { + return errors.ErrMalformedEntity + } if req.pageMeta.Limit < 1 || req.pageMeta.Offset < 0 { return errors.ErrInvalidQueryParams } diff --git a/readers/api/transport.go b/readers/api/transport.go index 5de8369614..bd64e4103d 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -194,6 +194,7 @@ func encodeError(_ context.Context, err error, w http.ResponseWriter) { switch { case errors.Contains(err, nil): case errors.Contains(err, errors.ErrInvalidQueryParams): + case errors.Contains(err, errors.ErrMalformedEntity): w.WriteHeader(http.StatusBadRequest) case errors.Contains(err, errors.ErrAuthentication): w.WriteHeader(http.StatusUnauthorized) From c18f6282978b84c5289b3b848740443c34ccc8a8 Mon Sep 17 00:00:00 2001 From: mteodor Date: Thu, 3 Feb 2022 11:49:28 +0100 Subject: [PATCH 31/39] fixing errors Signed-off-by: mteodor --- readers/api/endpoint_test.go | 6 +++--- readers/api/transport.go | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/readers/api/endpoint_test.go b/readers/api/endpoint_test.go index 9151c96acc..66d32b8228 100644 --- a/readers/api/endpoint_test.go +++ b/readers/api/endpoint_test.go @@ -202,7 +202,7 @@ func TestReadAll(t *testing.T) { desc: "read page with invalid token", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), token: fmt.Sprintf("Thing %s", invalid), - status: http.StatusForbidden, + status: http.StatusUnauthorized, }, { desc: "read page with multiple offset", @@ -220,7 +220,7 @@ func TestReadAll(t *testing.T) { desc: "read page with empty token", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), token: "", - status: http.StatusForbidden, + status: http.StatusUnauthorized, }, { desc: "read page with default offset", @@ -483,7 +483,7 @@ func TestReadAll(t *testing.T) { desc: "read page with invalid token", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", invalid), - status: http.StatusForbidden, + status: http.StatusUnauthorized, }, { desc: "read page with multiple offset", diff --git a/readers/api/transport.go b/readers/api/transport.go index bd64e4103d..955ab7c6ff 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -194,6 +194,7 @@ func encodeError(_ context.Context, err error, w http.ResponseWriter) { switch { case errors.Contains(err, nil): case errors.Contains(err, errors.ErrInvalidQueryParams): + w.WriteHeader(http.StatusBadRequest) case errors.Contains(err, errors.ErrMalformedEntity): w.WriteHeader(http.StatusBadRequest) case errors.Contains(err, errors.ErrAuthentication): @@ -218,9 +219,9 @@ func authorize(ctx context.Context, req listMessagesReq, tc mainflux.ThingsServi if user, err = usersAuth.Identify(ctx, &mainflux.Token{Value: token}); err != nil { e, ok := status.FromError(err) if ok && e.Code() == codes.PermissionDenied { - return errCannotAuthorizeUser + return errors.Wrap(errCannotAuthorizeUser, err) } - return errCannotAuthorizeUser + return err } if _, err = thingsAuth.IsChannelOwner(ctx, &mainflux.ChannelOwnerReq{Owner: user.Email, ChanID: req.chanID}); err != nil { e, ok := status.FromError(err) From fbadbb268aa06a362a3be56feef8175839572c35 Mon Sep 17 00:00:00 2001 From: mteodor Date: Sat, 5 Feb 2022 11:32:18 +0100 Subject: [PATCH 32/39] improve test case description Signed-off-by: mteodor --- readers/api/endpoint_test.go | 136 +++++++++++++++++------------------ 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/readers/api/endpoint_test.go b/readers/api/endpoint_test.go index 66d32b8228..eee2164979 100644 --- a/readers/api/endpoint_test.go +++ b/readers/api/endpoint_test.go @@ -153,7 +153,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with valid offset and limit", + desc: "read page with valid offset and limit as user", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -163,67 +163,67 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with negative offset", + desc: "read page with negative offset as thing", url: fmt.Sprintf("%s/channels/%s/messages?offset=-1&limit=10", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { - desc: "read page with negative limit", + desc: "read page with negative limit as thing", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=-10", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { - desc: "read page with zero limit", + desc: "read page with zero limit as thing", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=0", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { - desc: "read page with non-integer offset", + desc: "read page with non-integer offset as thing", url: fmt.Sprintf("%s/channels/%s/messages?offset=abc&limit=10", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { - desc: "read page with non-integer limit", + desc: "read page with non-integer limit as thing", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=abc", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { - desc: "read page with invalid channel id", + desc: "read page with invalid channel id as thing", url: fmt.Sprintf("%s/channels//messages?offset=0&limit=10", ts.URL), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { - desc: "read page with invalid token", + desc: "read page with invalid token as thing", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), token: fmt.Sprintf("Thing %s", invalid), status: http.StatusUnauthorized, }, { - desc: "read page with multiple offset", + desc: "read page with multiple offset as thing", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&offset=1&limit=10", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { - desc: "read page with multiple limit", + desc: "read page with multiple limit as thing", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=20&limit=10", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { - desc: "read page with empty token", + desc: "read page with empty token as thing", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), token: "", status: http.StatusUnauthorized, }, { - desc: "read page with default offset", + desc: "read page with default offset as thing", url: fmt.Sprintf("%s/channels/%s/messages?limit=10", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -233,7 +233,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with default limit", + desc: "read page with default limit as thing", url: fmt.Sprintf("%s/channels/%s/messages?offset=0", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -243,7 +243,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with senml format", + desc: "read page with senml format as thing", url: fmt.Sprintf("%s/channels/%s/messages?format=messages", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -253,7 +253,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with subtopic", + desc: "read page with subtopic as thing", url: fmt.Sprintf("%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, chanID, subtopic, httpProt), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -263,7 +263,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with subtopic and protocol", + desc: "read page with subtopic and protocol as thing", url: fmt.Sprintf("%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, chanID, subtopic, httpProt), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -273,7 +273,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with publisher", + desc: "read page with publisher as thing", url: fmt.Sprintf("%s/channels/%s/messages?publisher=%s", ts.URL, chanID, pubID2), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -283,7 +283,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with protocol", + desc: "read page with protocol as thing", url: fmt.Sprintf("%s/channels/%s/messages?protocol=http", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -293,7 +293,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with name", + desc: "read page with name as thing", url: fmt.Sprintf("%s/channels/%s/messages?name=%s", ts.URL, chanID, msgName), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -303,7 +303,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with value", + desc: "read page with value as thing", url: fmt.Sprintf("%s/channels/%s/messages?v=%f", ts.URL, chanID, v), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -313,7 +313,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with value and equal comparator", + desc: "read page with value and equal comparator as thing", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v, readers.EqualKey), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -323,7 +323,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with value and lower-than comparator", + desc: "read page with value and lower-than comparator as thing", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v+1, readers.LowerThanKey), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -333,7 +333,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with value and lower-than-or-equal comparator", + desc: "read page with value and lower-than-or-equal comparator as thing", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v+1, readers.LowerThanEqualKey), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -343,7 +343,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with value and greater-than comparator", + desc: "read page with value and greater-than comparator as thing", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v-1, readers.GreaterThanKey), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -353,7 +353,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with value and greater-than-or-equal comparator", + desc: "read page with value and greater-than-or-equal comparator as thing", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v-1, readers.GreaterThanEqualKey), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -363,19 +363,19 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with non-float value", + desc: "read page with non-float value as thing", url: fmt.Sprintf("%s/channels/%s/messages?v=ab01", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { - desc: "read page with value and wrong comparator", + desc: "read page with value and wrong comparator as thing", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=wrong", ts.URL, chanID, v-1), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { - desc: "read page with boolean value", + desc: "read page with boolean value as thing", url: fmt.Sprintf("%s/channels/%s/messages?vb=true", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -385,13 +385,13 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with non-boolean value", + desc: "read page with non-boolean value as thing", url: fmt.Sprintf("%s/channels/%s/messages?vb=yes", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { - desc: "read page with string value", + desc: "read page with string value as thing", url: fmt.Sprintf("%s/channels/%s/messages?vs=%s", ts.URL, chanID, vs), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -401,7 +401,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with data value", + desc: "read page with data value as thing", url: fmt.Sprintf("%s/channels/%s/messages?vd=%s", ts.URL, chanID, vd), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -411,20 +411,20 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with non-float from", + desc: "read page with non-float from as thing", url: fmt.Sprintf("%s/channels/%s/messages?from=ABCD", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { - desc: "read page with non-float to", + desc: "read page with non-float to as thing", url: fmt.Sprintf("%s/channels/%s/messages?to=ABCD", ts.URL, chanID), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusBadRequest, }, { - desc: "read page with from/to", + desc: "read page with from/to as thing", url: fmt.Sprintf("%s/channels/%s/messages?from=%f&to=%f", ts.URL, chanID, messages[19].Time, messages[4].Time), token: fmt.Sprintf("Thing %s", thingToken), status: http.StatusOK, @@ -434,7 +434,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with valid offset and limit", + desc: "read page with valid offset and limit as user", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -444,67 +444,67 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with negative offset", + desc: "read page with negative offset as user", url: fmt.Sprintf("%s/channels/%s/messages?offset=-1&limit=10", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { - desc: "read page with negative limit", + desc: "read page with negative limit as user", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=-10", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { - desc: "read page with zero limit", + desc: "read page with zero limit as user", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=0", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { - desc: "read page with non-integer offset", + desc: "read page with non-integer offset as user", url: fmt.Sprintf("%s/channels/%s/messages?offset=abc&limit=10", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { - desc: "read page with non-integer limit", + desc: "read page with non-integer limit as user", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=abc", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { - desc: "read page with invalid channel id", + desc: "read page with invalid channel id as user", url: fmt.Sprintf("%s/channels//messages?offset=0&limit=10", ts.URL), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { - desc: "read page with invalid token", + desc: "read page with invalid token as user", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", invalid), status: http.StatusUnauthorized, }, { - desc: "read page with multiple offset", + desc: "read page with multiple offset as user", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&offset=1&limit=10", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { - desc: "read page with multiple limit", + desc: "read page with multiple limit as user", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=20&limit=10", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { - desc: "read page with empty token", + desc: "read page with empty token as user", url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID), token: "", status: http.StatusUnauthorized, }, { - desc: "read page with default offset", + desc: "read page with default offset as user", url: fmt.Sprintf("%s/channels/%s/messages?limit=10", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -514,7 +514,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with default limit", + desc: "read page with default limit as user", url: fmt.Sprintf("%s/channels/%s/messages?offset=0", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -524,7 +524,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with senml format", + desc: "read page with senml format as user", url: fmt.Sprintf("%s/channels/%s/messages?format=messages", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -534,7 +534,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with subtopic", + desc: "read page with subtopic as user", url: fmt.Sprintf("%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, chanID, subtopic, httpProt), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -544,7 +544,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with subtopic and protocol", + desc: "read page with subtopic and protocol as user", url: fmt.Sprintf("%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, chanID, subtopic, httpProt), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -554,7 +554,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with publisher", + desc: "read page with publisher as user", url: fmt.Sprintf("%s/channels/%s/messages?publisher=%s", ts.URL, chanID, pubID2), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -564,7 +564,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with protocol", + desc: "read page with protocol as user", url: fmt.Sprintf("%s/channels/%s/messages?protocol=http", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -574,7 +574,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with name", + desc: "read page with name as user", url: fmt.Sprintf("%s/channels/%s/messages?name=%s", ts.URL, chanID, msgName), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -584,7 +584,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with value", + desc: "read page with value as user", url: fmt.Sprintf("%s/channels/%s/messages?v=%f", ts.URL, chanID, v), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -594,7 +594,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with value and equal comparator", + desc: "read page with value and equal comparator as user", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v, readers.EqualKey), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -604,7 +604,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with value and lower-than comparator", + desc: "read page with value and lower-than comparator as user", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v+1, readers.LowerThanKey), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -614,7 +614,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with value and lower-than-or-equal comparator", + desc: "read page with value and lower-than-or-equal comparator as user", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v+1, readers.LowerThanEqualKey), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -624,7 +624,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with value and greater-than comparator", + desc: "read page with value and greater-than comparator as user", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v-1, readers.GreaterThanKey), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -634,7 +634,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with value and greater-than-or-equal comparator", + desc: "read page with value and greater-than-or-equal comparator as user", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, chanID, v-1, readers.GreaterThanEqualKey), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -644,19 +644,19 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with non-float value", + desc: "read page with non-float value as user", url: fmt.Sprintf("%s/channels/%s/messages?v=ab01", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { - desc: "read page with value and wrong comparator", + desc: "read page with value and wrong comparator as user", url: fmt.Sprintf("%s/channels/%s/messages?v=%f&comparator=wrong", ts.URL, chanID, v-1), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { - desc: "read page with boolean value", + desc: "read page with boolean value as user", url: fmt.Sprintf("%s/channels/%s/messages?vb=true", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -666,13 +666,13 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with non-boolean value", + desc: "read page with non-boolean value as user", url: fmt.Sprintf("%s/channels/%s/messages?vb=yes", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { - desc: "read page with string value", + desc: "read page with string value as user", url: fmt.Sprintf("%s/channels/%s/messages?vs=%s", ts.URL, chanID, vs), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -682,7 +682,7 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with data value", + desc: "read page with data value as user", url: fmt.Sprintf("%s/channels/%s/messages?vd=%s", ts.URL, chanID, vd), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, @@ -692,20 +692,20 @@ func TestReadAll(t *testing.T) { }, }, { - desc: "read page with non-float from", + desc: "read page with non-float from as user", url: fmt.Sprintf("%s/channels/%s/messages?from=ABCD", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { - desc: "read page with non-float to", + desc: "read page with non-float to as user", url: fmt.Sprintf("%s/channels/%s/messages?to=ABCD", ts.URL, chanID), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusBadRequest, }, { - desc: "read page with from/to", + desc: "read page with from/to as user", url: fmt.Sprintf("%s/channels/%s/messages?from=%f&to=%f", ts.URL, chanID, messages[19].Time, messages[4].Time), token: fmt.Sprintf("Bearer %s", userToken), status: http.StatusOK, From 88edad226e74e99fa8c4f1404ddf7ecb94f9f4af Mon Sep 17 00:00:00 2001 From: mteodor Date: Sat, 5 Feb 2022 11:32:36 +0100 Subject: [PATCH 33/39] remove test code Signed-off-by: mteodor --- bootstrap/service_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bootstrap/service_test.go b/bootstrap/service_test.go index 4db795fd95..83fd98645e 100644 --- a/bootstrap/service_test.go +++ b/bootstrap/service_test.go @@ -9,7 +9,6 @@ import ( "crypto/cipher" "crypto/rand" "encoding/hex" - "encoding/json" "fmt" "io" "net/http/httptest" @@ -61,9 +60,6 @@ func newService(auth mainflux.AuthServiceClient, url string) bootstrap.Service { ThingsURL: url, } - t := map[string]interface{}{} - t["test"]= "test" - json.Marshal(t) sdk := mfsdk.NewSDK(config) return bootstrap.New(auth, things, sdk, encKey) } From 967e9c2c1367e507139049b66ebbb1f07d9083ae Mon Sep 17 00:00:00 2001 From: mteodor Date: Sat, 5 Feb 2022 11:33:08 +0100 Subject: [PATCH 34/39] dont inline Signed-off-by: mteodor --- readers/api/transport.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readers/api/transport.go b/readers/api/transport.go index 955ab7c6ff..1370088be7 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -215,8 +215,8 @@ func authorize(ctx context.Context, req listMessagesReq, tc mainflux.ThingsServi switch { case strings.HasPrefix(req.token, userToken): token := strings.TrimPrefix(req.token, userToken) - var user *mainflux.UserIdentity - if user, err = usersAuth.Identify(ctx, &mainflux.Token{Value: token}); err != nil { + user, err := usersAuth.Identify(ctx, &mainflux.Token{Value: token}) + if err != nil { e, ok := status.FromError(err) if ok && e.Code() == codes.PermissionDenied { return errors.Wrap(errCannotAuthorizeUser, err) From 4ef247a5be2f65bc242c01196892994ba2bee6fc Mon Sep 17 00:00:00 2001 From: mteodor Date: Tue, 8 Feb 2022 11:38:35 +0100 Subject: [PATCH 35/39] refactor a bit encodeError Signed-off-by: mteodor --- readers/api/transport.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/readers/api/transport.go b/readers/api/transport.go index 1370088be7..3577bc401b 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -193,9 +193,8 @@ func encodeResponse(_ context.Context, w http.ResponseWriter, response interface func encodeError(_ context.Context, err error, w http.ResponseWriter) { switch { case errors.Contains(err, nil): - case errors.Contains(err, errors.ErrInvalidQueryParams): - w.WriteHeader(http.StatusBadRequest) - case errors.Contains(err, errors.ErrMalformedEntity): + case errors.Contains(err, errors.ErrInvalidQueryParams), + errors.Contains(err, errors.ErrMalformedEntity): w.WriteHeader(http.StatusBadRequest) case errors.Contains(err, errors.ErrAuthentication): w.WriteHeader(http.StatusUnauthorized) From d0b5c8b12eaada9df7a0433e85c1a86ec4576247 Mon Sep 17 00:00:00 2001 From: mteodor Date: Tue, 8 Feb 2022 12:00:11 +0100 Subject: [PATCH 36/39] remove unused error Signed-off-by: mteodor --- readers/api/transport.go | 1 - 1 file changed, 1 deletion(-) diff --git a/readers/api/transport.go b/readers/api/transport.go index 3577bc401b..de36c545e2 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -44,7 +44,6 @@ const ( ) var ( - errEmptyToken = errors.New("missing token") errCannotAuthorizeUser = errors.New("authorization failed") errThingAccess = errors.New("thing has no permission") errUserAccess = errors.New("user has no permission") From ab541c40241523c20ed48bfe4c1c40d54fa12309 Mon Sep 17 00:00:00 2001 From: mteodor Date: Tue, 8 Feb 2022 15:20:03 +0100 Subject: [PATCH 37/39] remove unused error Signed-off-by: mteodor --- readers/api/transport.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/readers/api/transport.go b/readers/api/transport.go index de36c545e2..ae56952271 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -44,11 +44,10 @@ const ( ) var ( - errCannotAuthorizeUser = errors.New("authorization failed") - errThingAccess = errors.New("thing has no permission") - errUserAccess = errors.New("user has no permission") - thingsAuth mainflux.ThingsServiceClient - usersAuth mainflux.AuthServiceClient + errThingAccess = errors.New("thing has no permission") + errUserAccess = errors.New("user has no permission") + thingsAuth mainflux.ThingsServiceClient + usersAuth mainflux.AuthServiceClient ) // MakeHandler returns a HTTP handler for API endpoints. @@ -217,14 +216,14 @@ func authorize(ctx context.Context, req listMessagesReq, tc mainflux.ThingsServi if err != nil { e, ok := status.FromError(err) if ok && e.Code() == codes.PermissionDenied { - return errors.Wrap(errCannotAuthorizeUser, err) + return errors.Wrap(errUserAccess, err) } return err } if _, err = thingsAuth.IsChannelOwner(ctx, &mainflux.ChannelOwnerReq{Owner: user.Email, ChanID: req.chanID}); err != nil { e, ok := status.FromError(err) if ok && e.Code() == codes.PermissionDenied { - return errors.Wrap(errCannotAuthorizeUser, err) + return errors.Wrap(errUserAccess, err) } return err } From 50dad704d662517da7a6ad45de707726039e0e02 Mon Sep 17 00:00:00 2001 From: mteodor Date: Tue, 8 Feb 2022 17:19:56 +0100 Subject: [PATCH 38/39] fix things auth grpc url Signed-off-by: mteodor --- cmd/timescale-reader/main.go | 2 +- readers/timescale/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/timescale-reader/main.go b/cmd/timescale-reader/main.go index 5f9b5bd3c0..fb1fde5fac 100644 --- a/cmd/timescale-reader/main.go +++ b/cmd/timescale-reader/main.go @@ -48,7 +48,7 @@ const ( defDBSSLKey = "" defDBSSLRootCert = "" defJaegerURL = "" - defThingsAuthURL = "localhost:8181" + defThingsAuthURL = "localhost:8183" defThingsAuthTimeout = "1s" envLogLevel = "MF_TIMESCALE_READER_LOG_LEVEL" diff --git a/readers/timescale/README.md b/readers/timescale/README.md index 2c4f11e49e..10f4b4dfe7 100644 --- a/readers/timescale/README.md +++ b/readers/timescale/README.md @@ -24,7 +24,7 @@ default values. | MF_TIMESCALE_READER_DB_SSL_KEY | Timescale SSL key | "" | | MF_TIMESCALE_READER_DB_SSL_ROOT_CERT | Timescale SSL root certificate path | "" | | MF_JAEGER_URL | Jaeger server URL | localhost:6831 | -| MF_THINGS_AUTH_GRPC_URL | Things service Auth gRPC URL | localhost:8181 | +| MF_THINGS_AUTH_GRPC_URL | Things service Auth gRPC URL | localhost:8183 | | MF_THINGS_AUTH_GRPC_TIMEOUT | Things service Auth gRPC timeout in seconds | 1s | ## Deployment From 6a6b930ddeecf1aa0097afcc9346316f9e0f883d Mon Sep 17 00:00:00 2001 From: mteodor Date: Wed, 9 Feb 2022 16:01:15 +0100 Subject: [PATCH 39/39] rename variables for header prefix Signed-off-by: mteodor --- readers/api/transport.go | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/readers/api/transport.go b/readers/api/transport.go index ae56952271..b97f2c59c8 100644 --- a/readers/api/transport.go +++ b/readers/api/transport.go @@ -22,25 +22,25 @@ import ( ) const ( - contentType = "application/json" - offsetKey = "offset" - limitKey = "limit" - formatKey = "format" - subtopicKey = "subtopic" - publisherKey = "publisher" - protocolKey = "protocol" - nameKey = "name" - valueKey = "v" - stringValueKey = "vs" - dataValueKey = "vd" - comparatorKey = "comparator" - fromKey = "from" - toKey = "to" - defLimit = 10 - defOffset = 0 - defFormat = "messages" - thingToken = "Thing " - userToken = "Bearer " + contentType = "application/json" + offsetKey = "offset" + limitKey = "limit" + formatKey = "format" + subtopicKey = "subtopic" + publisherKey = "publisher" + protocolKey = "protocol" + nameKey = "name" + valueKey = "v" + stringValueKey = "vs" + dataValueKey = "vd" + comparatorKey = "comparator" + fromKey = "from" + toKey = "to" + defLimit = 10 + defOffset = 0 + defFormat = "messages" + thingTokenPrefix = "Thing " + userTokenPrefix = "Bearer " ) var ( @@ -210,8 +210,8 @@ func encodeError(_ context.Context, err error, w http.ResponseWriter) { func authorize(ctx context.Context, req listMessagesReq, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient) (err error) { switch { - case strings.HasPrefix(req.token, userToken): - token := strings.TrimPrefix(req.token, userToken) + case strings.HasPrefix(req.token, userTokenPrefix): + token := strings.TrimPrefix(req.token, userTokenPrefix) user, err := usersAuth.Identify(ctx, &mainflux.Token{Value: token}) if err != nil { e, ok := status.FromError(err) @@ -229,7 +229,7 @@ func authorize(ctx context.Context, req listMessagesReq, tc mainflux.ThingsServi } return nil default: - token := strings.TrimPrefix(req.token, thingToken) + token := strings.TrimPrefix(req.token, thingTokenPrefix) if _, err := thingsAuth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: req.chanID}); err != nil { return errors.Wrap(errThingAccess, err) }