MG-2507 - Update auth in readers service (#2514)

Signed-off-by: Felix Gateru <felix.gateru@gmail.com>
This commit is contained in:
Felix Gateru
2024-11-10 20:59:46 +03:00
committed by GitHub
parent a5059a7946
commit 3a02788e3a
14 changed files with 238 additions and 156 deletions
+10 -1
View File
@@ -35,7 +35,7 @@ tags:
url: https://docs.magistrala.abstractmachines.fr/
paths:
/channels/{chanId}/messages:
/{domainID}/channels/{chanId}/messages:
get:
operationId: getMessages
summary: Retrieves messages sent to single channel
@@ -47,6 +47,7 @@ paths:
tags:
- readers
parameters:
- $ref: "#/components/parameters/DomainID"
- $ref: "#/components/parameters/ChanId"
- $ref: "#/components/parameters/Limit"
- $ref: "#/components/parameters/Offset"
@@ -141,6 +142,14 @@ components:
description: Time of updating measurement.
parameters:
DomainID:
name: domainID
description: Unique domain identifier.
in: path
schema:
type: string
format: uuid
required: true
ChanId:
name: chanId
description: Unique channel identifier.
+4 -4
View File
@@ -28,13 +28,13 @@ var cmdMessages = []cobra.Command{
},
},
{
Use: "read <channel_id.subtopic> <user_token>",
Use: "read <channel_id.subtopic> <domain_id> <user_token>",
Short: "Read messages",
Long: "Reads all channel messages\n" +
"Usage:\n" +
"\tmagistrala-cli messages read <channel_id.subtopic> <user_token> --offset <offset> --limit <limit> - lists all messages with provided offset and limit\n",
"\tmagistrala-cli messages read <channel_id.subtopic> <domain_id> <user_token> --offset <offset> --limit <limit> - lists all messages with provided offset and limit\n",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 2 {
if len(args) != 3 {
logUsageCmd(*cmd, cmd.Use)
return
}
@@ -45,7 +45,7 @@ var cmdMessages = []cobra.Command{
},
}
m, err := sdk.ReadMessages(pageMetadata, args[0], args[1])
m, err := sdk.ReadMessages(pageMetadata, args[0], args[1], args[2])
if err != nil {
logErrorCmd(*cmd, err)
return
+4 -1
View File
@@ -104,6 +104,7 @@ func TestReadMesageCmd(t *testing.T) {
desc: "read message successfully",
args: []string{
channel.ID,
domainID,
validToken,
},
page: mgsdk.MessagesPage{
@@ -124,6 +125,7 @@ func TestReadMesageCmd(t *testing.T) {
desc: "read message with invalid args",
args: []string{
channel.ID,
domainID,
validToken,
extraArg,
},
@@ -133,6 +135,7 @@ func TestReadMesageCmd(t *testing.T) {
desc: "read message with invalid token",
args: []string{
channel.ID,
domainID,
invalidToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized),
@@ -143,7 +146,7 @@ func TestReadMesageCmd(t *testing.T) {
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("ReadMessages", mock.Anything, tc.args[0], tc.args[1]).Return(tc.page, tc.sdkErr)
sdkCall := sdkMock.On("ReadMessages", mock.Anything, tc.args[0], tc.args[1], tc.args[2]).Return(tc.page, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{readCmd}, tc.args...)...)
switch tc.logType {
+14 -4
View File
@@ -14,6 +14,7 @@ import (
chclient "github.com/absmach/callhome/pkg/client"
"github.com/absmach/magistrala"
mglog "github.com/absmach/magistrala/logger"
authsvcAuthn "github.com/absmach/magistrala/pkg/authn/authsvc"
"github.com/absmach/magistrala/pkg/authz/authsvc"
"github.com/absmach/magistrala/pkg/grpcclient"
pgclient "github.com/absmach/magistrala/pkg/postgres"
@@ -84,14 +85,14 @@ func main() {
}
defer db.Close()
authzCfg := grpcclient.Config{}
if err := env.ParseWithOptions(&authzCfg, env.Options{Prefix: envPrefixAuth}); err != nil {
clientCfg := grpcclient.Config{}
if err := env.ParseWithOptions(&clientCfg, env.Options{Prefix: envPrefixAuth}); err != nil {
logger.Error(fmt.Sprintf("failed to load auth gRPC client configuration : %s", err))
exitCode = 1
return
}
authz, authzHandler, err := authsvc.NewAuthorization(ctx, authzCfg)
authz, authzHandler, err := authsvc.NewAuthorization(ctx, clientCfg)
if err != nil {
logger.Error(err.Error())
exitCode = 1
@@ -100,6 +101,15 @@ func main() {
defer authzHandler.Close()
logger.Info("Authz successfully connected to auth gRPC server " + authzHandler.Secure())
authn, authnHandler, err := authsvcAuthn.NewAuthentication(ctx, clientCfg)
if err != nil {
logger.Error(err.Error())
exitCode = 1
return
}
defer authnHandler.Close()
logger.Info("Authn successfully connected to auth gRPC server " + authnHandler.Secure())
thingsClientCfg := grpcclient.Config{}
if err := env.ParseWithOptions(&thingsClientCfg, env.Options{Prefix: envPrefixThings}); err != nil {
logger.Error(fmt.Sprintf("failed to load %s auth configuration : %s", svcName, err))
@@ -125,7 +135,7 @@ func main() {
exitCode = 1
return
}
hs := httpserver.NewServer(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, authz, thingsClient, svcName, cfg.InstanceID), logger)
hs := httpserver.NewServer(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, authn, authz, thingsClient, svcName, cfg.InstanceID), logger)
if cfg.SendTelemetry {
chc := chclient.New(svcName, magistrala.Version, logger, cancel)
+14 -4
View File
@@ -14,6 +14,7 @@ import (
chclient "github.com/absmach/callhome/pkg/client"
"github.com/absmach/magistrala"
mglog "github.com/absmach/magistrala/logger"
authsvcAuthn "github.com/absmach/magistrala/pkg/authn/authsvc"
"github.com/absmach/magistrala/pkg/authz/authsvc"
"github.com/absmach/magistrala/pkg/grpcclient"
pgclient "github.com/absmach/magistrala/pkg/postgres"
@@ -84,14 +85,14 @@ func main() {
repo := newService(db, logger)
authzCfg := grpcclient.Config{}
if err := env.ParseWithOptions(&authzCfg, env.Options{Prefix: envPrefixAuth}); err != nil {
clientCfg := grpcclient.Config{}
if err := env.ParseWithOptions(&clientCfg, env.Options{Prefix: envPrefixAuth}); err != nil {
logger.Error(fmt.Sprintf("failed to load auth gRPC client configuration : %s", err))
exitCode = 1
return
}
authz, authzHandler, err := authsvc.NewAuthorization(ctx, authzCfg)
authz, authzHandler, err := authsvc.NewAuthorization(ctx, clientCfg)
if err != nil {
logger.Error(err.Error())
exitCode = 1
@@ -100,6 +101,15 @@ func main() {
defer authzHandler.Close()
logger.Info("Authz successfully connected to auth gRPC server " + authzHandler.Secure())
authn, authnHandler, err := authsvcAuthn.NewAuthentication(ctx, clientCfg)
if err != nil {
logger.Error(err.Error())
exitCode = 1
return
}
defer authnHandler.Close()
logger.Info("Authn successfully connected to auth gRPC server " + authnHandler.Secure())
thingsClientCfg := grpcclient.Config{}
if err := env.ParseWithOptions(&thingsClientCfg, env.Options{Prefix: envPrefixThings}); err != nil {
logger.Error(fmt.Sprintf("failed to load %s auth configuration : %s", svcName, err))
@@ -123,7 +133,7 @@ func main() {
exitCode = 1
return
}
hs := httpserver.NewServer(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, authz, thingsClient, svcName, cfg.InstanceID), logger)
hs := httpserver.NewServer(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, authn, authz, thingsClient, svcName, cfg.InstanceID), logger)
if cfg.SendTelemetry {
chc := chclient.New(svcName, magistrala.Version, logger, cancel)
+15 -1
View File
@@ -13,8 +13,12 @@ import (
bmocks "github.com/absmach/magistrala/bootstrap/mocks"
mglog "github.com/absmach/magistrala/logger"
authnmocks "github.com/absmach/magistrala/pkg/authn/mocks"
authzmocks "github.com/absmach/magistrala/pkg/authz/mocks"
"github.com/absmach/magistrala/pkg/errors"
sdk "github.com/absmach/magistrala/pkg/sdk/go"
readersapi "github.com/absmach/magistrala/readers/api"
readersmocks "github.com/absmach/magistrala/readers/mocks"
thmocks "github.com/absmach/magistrala/things/mocks"
"github.com/stretchr/testify/assert"
)
@@ -31,7 +35,7 @@ func TestHealth(t *testing.T) {
bootstrapTs := setupMinimalBootstrap()
defer bootstrapTs.Close()
readerTs, _, _ := setupReader()
readerTs := setupMinimalReader()
defer readerTs.Close()
httpAdapterTs, _, _ := setupMessages()
@@ -128,3 +132,13 @@ func setupMinimalBootstrap() *httptest.Server {
return httptest.NewServer(mux)
}
func setupMinimalReader() *httptest.Server {
repo := new(readersmocks.MessageRepository)
authz := new(authzmocks.Authorization)
authn := new(authnmocks.Authentication)
things := new(thmocks.ThingsServiceClient)
mux := readersapi.MakeHandler(repo, authn, authz, things, "test", "")
return httptest.NewServer(mux)
}
+2 -2
View File
@@ -32,7 +32,7 @@ func (sdk mgSDK) SendMessage(chanName, msg, key string) errors.SDKError {
return err
}
func (sdk mgSDK) ReadMessages(pm MessagePageMetadata, chanName, token string) (MessagesPage, errors.SDKError) {
func (sdk mgSDK) ReadMessages(pm MessagePageMetadata, chanName, domainID, token string) (MessagesPage, errors.SDKError) {
chanNameParts := strings.SplitN(chanName, ".", channelParts)
chanID := chanNameParts[0]
subtopicPart := ""
@@ -40,7 +40,7 @@ func (sdk mgSDK) ReadMessages(pm MessagePageMetadata, chanName, token string) (M
subtopicPart = fmt.Sprintf("?subtopic=%s", chanNameParts[1])
}
readMessagesEndpoint := fmt.Sprintf("channels/%s/messages%s", chanID, subtopicPart)
readMessagesEndpoint := fmt.Sprintf("%s/channels/%s/messages%s", domainID, chanID, subtopicPart)
msgURL, err := sdk.withMessageQueryParams(sdk.readerURL, readMessagesEndpoint, pm)
if err != nil {
return MessagesPage{}, errors.NewSDKError(err)
+24 -9
View File
@@ -14,6 +14,8 @@ import (
"github.com/absmach/magistrala/http/api"
mglog "github.com/absmach/magistrala/logger"
"github.com/absmach/magistrala/pkg/apiutil"
mgauthn "github.com/absmach/magistrala/pkg/authn"
authnmocks "github.com/absmach/magistrala/pkg/authn/mocks"
authzmocks "github.com/absmach/magistrala/pkg/authz/mocks"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
@@ -50,13 +52,14 @@ func setupMessages() (*httptest.Server, *thmocks.ThingsServiceClient, *pubsub.Pu
return httptest.NewServer(http.HandlerFunc(mp.ServeHTTP)), things, pub
}
func setupReader() (*httptest.Server, *authzmocks.Authorization, *readersmocks.MessageRepository) {
func setupReader() (*httptest.Server, *authzmocks.Authorization, *authnmocks.Authentication, *readersmocks.MessageRepository) {
repo := new(readersmocks.MessageRepository)
authz := new(authzmocks.Authorization)
authn := new(authnmocks.Authentication)
things := new(thmocks.ThingsServiceClient)
mux := readersapi.MakeHandler(repo, authz, things, "test", "")
return httptest.NewServer(mux), authz, repo
mux := readersapi.MakeHandler(repo, authn, authz, things, "test", "")
return httptest.NewServer(mux), authz, authn, repo
}
func TestSendMessage(t *testing.T) {
@@ -196,7 +199,7 @@ func TestSetContentType(t *testing.T) {
}
func TestReadMessages(t *testing.T) {
ts, authz, repo := setupReader()
ts, authz, authn, repo := setupReader()
defer ts.Close()
channelID := "channelID"
@@ -220,8 +223,10 @@ func TestReadMessages(t *testing.T) {
desc string
token string
chanName string
domainID string
messagePageMeta sdk.MessagePageMetadata
authErr error
authzErr error
authnErr error
repoRes readers.MessagesPage
repoErr error
response sdk.MessagesPage
@@ -231,6 +236,7 @@ func TestReadMessages(t *testing.T) {
desc: "read messages successfully",
token: validToken,
chanName: channelID,
domainID: validID,
messagePageMeta: sdk.MessagePageMetadata{
PageMetadata: sdk.PageMetadata{
Offset: 0,
@@ -257,6 +263,7 @@ func TestReadMessages(t *testing.T) {
desc: "read messages successfully with subtopic",
token: validToken,
chanName: channelID + ".subtopic",
domainID: validID,
messagePageMeta: sdk.MessagePageMetadata{
PageMetadata: sdk.PageMetadata{
Offset: 0,
@@ -281,6 +288,7 @@ func TestReadMessages(t *testing.T) {
desc: "read messages with invalid token",
token: invalidToken,
chanName: channelID,
domainID: validID,
messagePageMeta: sdk.MessagePageMetadata{
PageMetadata: sdk.PageMetadata{
Offset: 0,
@@ -289,7 +297,7 @@ func TestReadMessages(t *testing.T) {
Subtopic: "subtopic",
Publisher: validID,
},
authErr: svcerr.ErrAuthorization,
authzErr: svcerr.ErrAuthorization,
repoRes: readers.MessagesPage{},
response: sdk.MessagesPage{},
err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrAuthorization, svcerr.ErrAuthorization), http.StatusUnauthorized),
@@ -298,6 +306,7 @@ func TestReadMessages(t *testing.T) {
desc: "read messages with empty token",
token: "",
chanName: channelID,
domainID: validID,
messagePageMeta: sdk.MessagePageMetadata{
PageMetadata: sdk.PageMetadata{
Offset: 0,
@@ -306,7 +315,7 @@ func TestReadMessages(t *testing.T) {
Subtopic: "subtopic",
Publisher: validID,
},
authErr: svcerr.ErrAuthorization,
authnErr: svcerr.ErrAuthentication,
repoRes: readers.MessagesPage{},
response: sdk.MessagesPage{},
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized),
@@ -315,6 +324,7 @@ func TestReadMessages(t *testing.T) {
desc: "read messages with empty channel ID",
token: validToken,
chanName: "",
domainID: validID,
messagePageMeta: sdk.MessagePageMetadata{
PageMetadata: sdk.PageMetadata{
Offset: 0,
@@ -332,6 +342,7 @@ func TestReadMessages(t *testing.T) {
desc: "read messages with invalid message page metadata",
token: validToken,
chanName: channelID,
domainID: validID,
messagePageMeta: sdk.MessagePageMetadata{
PageMetadata: sdk.PageMetadata{
Offset: 0,
@@ -352,6 +363,7 @@ func TestReadMessages(t *testing.T) {
desc: "read messages with response that cannot be unmarshalled",
token: validToken,
chanName: channelID,
domainID: validID,
messagePageMeta: sdk.MessagePageMetadata{
PageMetadata: sdk.PageMetadata{
Offset: 0,
@@ -371,9 +383,11 @@ func TestReadMessages(t *testing.T) {
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
authCall := authz.On("Authorize", mock.Anything, mock.Anything).Return(tc.authErr)
authCall := authz.On("Authorize", mock.Anything, mock.Anything).Return(tc.authzErr)
authCall1 := authn.On("Authenticate", mock.Anything, tc.token).Return(mgauthn.Session{UserID: validID}, tc.authnErr)
repoCall := repo.On("ReadAll", channelID, mock.Anything).Return(tc.repoRes, tc.repoErr)
response, err := mgsdk.ReadMessages(tc.messagePageMeta, tc.chanName, tc.token)
response, err := mgsdk.ReadMessages(tc.messagePageMeta, tc.chanName, tc.domainID, tc.token)
fmt.Println(err)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, response)
if tc.err == nil {
@@ -381,6 +395,7 @@ func TestReadMessages(t *testing.T) {
assert.True(t, ok)
}
authCall.Unset()
authCall1.Unset()
repoCall.Unset()
})
}
+2 -2
View File
@@ -884,9 +884,9 @@ type SDK interface {
// Offset: 0,
// Limit: 10,
// }
// msgs, _ := sdk.ReadMessages(pm,"channelID", "token")
// msgs, _ := sdk.ReadMessages(pm,"channelID", "domainID", "token")
// fmt.Println(msgs)
ReadMessages(pm MessagePageMetadata, chanID, token string) (MessagesPage, errors.SDKError)
ReadMessages(pm MessagePageMetadata, chanID, domainID, token string) (MessagesPage, errors.SDKError)
// SetContentType sets message content type.
//
+9 -9
View File
@@ -1820,9 +1820,9 @@ func (_m *SDK) Parents(id string, pm sdk.PageMetadata, domainID string, token st
return r0, r1
}
// ReadMessages provides a mock function with given fields: pm, chanID, token
func (_m *SDK) ReadMessages(pm sdk.MessagePageMetadata, chanID string, token string) (sdk.MessagesPage, errors.SDKError) {
ret := _m.Called(pm, chanID, token)
// ReadMessages provides a mock function with given fields: pm, chanID, domainID, token
func (_m *SDK) ReadMessages(pm sdk.MessagePageMetadata, chanID string, domainID string, token string) (sdk.MessagesPage, errors.SDKError) {
ret := _m.Called(pm, chanID, domainID, token)
if len(ret) == 0 {
panic("no return value specified for ReadMessages")
@@ -1830,17 +1830,17 @@ func (_m *SDK) ReadMessages(pm sdk.MessagePageMetadata, chanID string, token str
var r0 sdk.MessagesPage
var r1 errors.SDKError
if rf, ok := ret.Get(0).(func(sdk.MessagePageMetadata, string, string) (sdk.MessagesPage, errors.SDKError)); ok {
return rf(pm, chanID, token)
if rf, ok := ret.Get(0).(func(sdk.MessagePageMetadata, string, string, string) (sdk.MessagesPage, errors.SDKError)); ok {
return rf(pm, chanID, domainID, token)
}
if rf, ok := ret.Get(0).(func(sdk.MessagePageMetadata, string, string) sdk.MessagesPage); ok {
r0 = rf(pm, chanID, token)
if rf, ok := ret.Get(0).(func(sdk.MessagePageMetadata, string, string, string) sdk.MessagesPage); ok {
r0 = rf(pm, chanID, domainID, token)
} else {
r0 = ret.Get(0).(sdk.MessagesPage)
}
if rf, ok := ret.Get(1).(func(sdk.MessagePageMetadata, string, string) errors.SDKError); ok {
r1 = rf(pm, chanID, token)
if rf, ok := ret.Get(1).(func(sdk.MessagePageMetadata, string, string, string) errors.SDKError); ok {
r1 = rf(pm, chanID, domainID, token)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(errors.SDKError)
+3 -2
View File
@@ -8,6 +8,7 @@ import (
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/pkg/apiutil"
mgauthn "github.com/absmach/magistrala/pkg/authn"
mgauthz "github.com/absmach/magistrala/pkg/authz"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
@@ -15,14 +16,14 @@ import (
"github.com/go-kit/kit/endpoint"
)
func listMessagesEndpoint(svc readers.MessageRepository, authz mgauthz.Authorization, thingsClient magistrala.ThingsServiceClient) endpoint.Endpoint {
func listMessagesEndpoint(svc readers.MessageRepository, authn mgauthn.Authentication, authz mgauthz.Authorization, thingsClient magistrala.ThingsServiceClient) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(listMessagesReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
if err := authorize(ctx, req, authz, thingsClient); err != nil {
if err := authorize(ctx, req, authn, authz, thingsClient); err != nil {
return nil, errors.Wrap(svcerr.ErrAuthorization, err)
}
+110 -96
View File
@@ -14,6 +14,8 @@ import (
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/pkg/apiutil"
mgauthn "github.com/absmach/magistrala/pkg/authn"
authnmocks "github.com/absmach/magistrala/pkg/authn/mocks"
authzmocks "github.com/absmach/magistrala/pkg/authz/mocks"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
"github.com/absmach/magistrala/pkg/transformers/senml"
@@ -42,15 +44,17 @@ const (
)
var (
v float64 = 5
vs = "value"
vb = true
vd = "dataValue"
sum float64 = 42
v float64 = 5
vs = "value"
vb = true
vd = "dataValue"
sum float64 = 42
domainID = testsutil.GenerateUUID(&testing.T{})
validSession = mgauthn.Session{UserID: testsutil.GenerateUUID(&testing.T{})}
)
func newServer(repo *mocks.MessageRepository, authz *authzmocks.Authorization, thingsAuthzClient *thmocks.ThingsServiceClient) *httptest.Server {
mux := api.MakeHandler(repo, authz, thingsAuthzClient, svcName, instanceID)
func newServer(repo *mocks.MessageRepository, authn *authnmocks.Authentication, authz *authzmocks.Authorization, thingsAuthzClient *thmocks.ThingsServiceClient) *httptest.Server {
mux := api.MakeHandler(repo, authn, authz, thingsAuthzClient, svcName, instanceID)
return httptest.NewServer(mux)
}
@@ -129,8 +133,9 @@ func TestReadAll(t *testing.T) {
repo := new(mocks.MessageRepository)
authz := new(authzmocks.Authorization)
authn := new(authnmocks.Authentication)
things := new(thmocks.ThingsServiceClient)
ts := newServer(repo, authz, things)
ts := newServer(repo, authn, authz, things)
defer ts.Close()
cases := []struct {
@@ -142,11 +147,12 @@ func TestReadAll(t *testing.T) {
authResponse bool
status int
res pageRes
authnErr error
err error
}{
{
desc: "read page with valid offset and limit",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -158,7 +164,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with valid offset and limit as user",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -168,73 +174,80 @@ func TestReadAll(t *testing.T) {
Messages: messages[0:10],
},
},
{
desc: "read page as user without domain id",
url: fmt.Sprintf("%s/%s/channels/%s/messages", ts.URL, "", chanID),
token: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with negative offset as thing",
url: fmt.Sprintf("%s/channels/%s/messages?offset=-1&limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=-1&limit=10", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with negative limit as thing",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=-10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=-10", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with zero limit as thing",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=0", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=0", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with non-integer offset as thing",
url: fmt.Sprintf("%s/channels/%s/messages?offset=abc&limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=abc&limit=10", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with non-integer limit as thing",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=abc", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=abc", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with invalid channel id as thing",
url: fmt.Sprintf("%s/channels//messages?offset=0&limit=10", ts.URL),
url: fmt.Sprintf("%s/%s/channels//messages?offset=0&limit=10", ts.URL, ""),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with multiple offset as thing",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&offset=1&limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&offset=1&limit=10", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with multiple limit as thing",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=20&limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=20&limit=10", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with empty token as thing",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, "", chanID),
token: "",
authResponse: false,
authnErr: svcerr.ErrAuthentication,
status: http.StatusUnauthorized,
err: svcerr.ErrAuthorization,
err: svcerr.ErrAuthentication,
},
{
desc: "read page with default offset as thing",
url: fmt.Sprintf("%s/channels/%s/messages?limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?limit=10", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -246,7 +259,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with default limit as thing",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -258,7 +271,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with senml format as thing",
url: fmt.Sprintf("%s/channels/%s/messages?format=messages", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?format=messages", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -270,7 +283,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with subtopic as thing",
url: fmt.Sprintf("%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, chanID, subtopic, httpProt),
url: fmt.Sprintf("%s/%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, "", chanID, subtopic, httpProt),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -282,7 +295,7 @@ func TestReadAll(t *testing.T) {
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, "", chanID, subtopic, httpProt),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -294,7 +307,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with publisher as thing",
url: fmt.Sprintf("%s/channels/%s/messages?publisher=%s", ts.URL, chanID, pubID2),
url: fmt.Sprintf("%s/%s/channels/%s/messages?publisher=%s", ts.URL, "", chanID, pubID2),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -304,10 +317,9 @@ func TestReadAll(t *testing.T) {
Messages: queryMsgs[0:10],
},
},
{
desc: "read page with protocol as thing",
url: fmt.Sprintf("%s/channels/%s/messages?protocol=http", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?protocol=http", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -319,7 +331,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with name as thing",
url: fmt.Sprintf("%s/channels/%s/messages?name=%s", ts.URL, chanID, msgName),
url: fmt.Sprintf("%s/%s/channels/%s/messages?name=%s", ts.URL, "", chanID, msgName),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -331,7 +343,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with value as thing",
url: fmt.Sprintf("%s/channels/%s/messages?v=%f", ts.URL, chanID, v),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f", ts.URL, "", chanID, v),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -343,7 +355,7 @@ func TestReadAll(t *testing.T) {
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, "", chanID, v, readers.EqualKey),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -355,7 +367,7 @@ func TestReadAll(t *testing.T) {
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, "", chanID, v+1, readers.LowerThanKey),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -367,7 +379,7 @@ func TestReadAll(t *testing.T) {
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, "", chanID, v+1, readers.LowerThanEqualKey),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -379,7 +391,7 @@ func TestReadAll(t *testing.T) {
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, "", chanID, v-1, readers.GreaterThanKey),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -391,7 +403,7 @@ func TestReadAll(t *testing.T) {
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, "", chanID, v-1, readers.GreaterThanEqualKey),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -403,21 +415,21 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with non-float value as thing",
url: fmt.Sprintf("%s/channels/%s/messages?v=ab01", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=ab01", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=wrong", ts.URL, "", chanID, v-1),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with boolean value as thing",
url: fmt.Sprintf("%s/channels/%s/messages?vb=true", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?vb=true", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -429,14 +441,14 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with non-boolean value as thing",
url: fmt.Sprintf("%s/channels/%s/messages?vb=yes", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?vb=yes", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with string value as thing",
url: fmt.Sprintf("%s/channels/%s/messages?vs=%s", ts.URL, chanID, vs),
url: fmt.Sprintf("%s/%s/channels/%s/messages?vs=%s", ts.URL, "", chanID, vs),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -448,7 +460,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with data value as thing",
url: fmt.Sprintf("%s/channels/%s/messages?vd=%s", ts.URL, chanID, vd),
url: fmt.Sprintf("%s/%s/channels/%s/messages?vd=%s", ts.URL, "", chanID, vd),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -460,21 +472,21 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with non-float from as thing",
url: fmt.Sprintf("%s/channels/%s/messages?from=ABCD", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?from=ABCD", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with non-float to as thing",
url: fmt.Sprintf("%s/channels/%s/messages?to=ABCD", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?to=ABCD", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?from=%f&to=%f", ts.URL, "", chanID, messages[19].Time, messages[4].Time),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -486,14 +498,14 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with aggregation as thing",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=MAX", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with interval as thing",
url: fmt.Sprintf("%s/channels/%s/messages?interval=10h", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?interval=10h", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -505,14 +517,14 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with aggregation and interval as thing",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=MAX&interval=10h", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h", ts.URL, "", chanID),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval, to and from as thing",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=MAX&interval=10h&from=%f&to=%f", ts.URL, chanID, messages[19].Time, messages[4].Time),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&from=%f&to=%f", ts.URL, "", chanID, messages[19].Time, messages[4].Time),
key: thingToken,
authResponse: true,
status: http.StatusOK,
@@ -524,42 +536,42 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with invalid aggregation and valid interval, to and from as thing",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=invalid&interval=10h&from=%f&to=%f", ts.URL, chanID, messages[19].Time, messages[4].Time),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=invalid&interval=10h&from=%f&to=%f", ts.URL, "", chanID, messages[19].Time, messages[4].Time),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with invalid interval and valid aggregation, to and from as thing",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=MAX&interval=10hrs&from=%f&to=%f", ts.URL, chanID, messages[19].Time, messages[4].Time),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10hrs&from=%f&to=%f", ts.URL, "", chanID, messages[19].Time, messages[4].Time),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval and to with missing from as thing",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=MAX&interval=10h&to=%f", ts.URL, chanID, messages[4].Time),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&to=%f", ts.URL, "", chanID, messages[4].Time),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval and to with invalid from as thing",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=MAX&interval=10h&to=ABCD&from=%f", ts.URL, chanID, messages[4].Time),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&to=ABCD&from=%f", ts.URL, "", chanID, messages[4].Time),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval and to with invalid to as thing",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=MAX&interval=10h&from=%f&to=ABCD", ts.URL, chanID, messages[4].Time),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&from=%f&to=ABCD", ts.URL, "", chanID, messages[4].Time),
key: thingToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with valid offset and limit as user",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -571,49 +583,49 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with negative offset as user",
url: fmt.Sprintf("%s/channels/%s/messages?offset=-1&limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=-1&limit=10", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with negative limit as user",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=-10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=-10", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with zero limit as user",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=0", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=0", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with non-integer offset as user",
url: fmt.Sprintf("%s/channels/%s/messages?offset=abc&limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=abc&limit=10", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with non-integer limit as user",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=abc", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=abc", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with invalid channel id as user",
url: fmt.Sprintf("%s/channels//messages?offset=0&limit=10", ts.URL),
url: fmt.Sprintf("%s/%s/channels//messages?offset=0&limit=10", ts.URL, domainID),
token: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with invalid token as user",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
token: invalidToken,
authResponse: false,
status: http.StatusUnauthorized,
@@ -621,21 +633,21 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with multiple offset as user",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&offset=1&limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&offset=1&limit=10", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with multiple limit as user",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=20&limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=20&limit=10", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with empty token as user",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
token: "",
authResponse: false,
status: http.StatusUnauthorized,
@@ -643,7 +655,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with default offset as user",
url: fmt.Sprintf("%s/channels/%s/messages?limit=10", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?limit=10", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -655,7 +667,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with default limit as user",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -667,7 +679,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with senml format as user",
url: fmt.Sprintf("%s/channels/%s/messages?format=messages", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?format=messages", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -679,7 +691,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with subtopic as user",
url: fmt.Sprintf("%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, chanID, subtopic, httpProt),
url: fmt.Sprintf("%s/%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, domainID, chanID, subtopic, httpProt),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -691,7 +703,7 @@ func TestReadAll(t *testing.T) {
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, domainID, chanID, subtopic, httpProt),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -703,7 +715,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with publisher as user",
url: fmt.Sprintf("%s/channels/%s/messages?publisher=%s", ts.URL, chanID, pubID2),
url: fmt.Sprintf("%s/%s/channels/%s/messages?publisher=%s", ts.URL, domainID, chanID, pubID2),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -715,7 +727,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with protocol as user",
url: fmt.Sprintf("%s/channels/%s/messages?protocol=http", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?protocol=http", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -727,7 +739,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with name as user",
url: fmt.Sprintf("%s/channels/%s/messages?name=%s", ts.URL, chanID, msgName),
url: fmt.Sprintf("%s/%s/channels/%s/messages?name=%s", ts.URL, domainID, chanID, msgName),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -739,7 +751,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with value as user",
url: fmt.Sprintf("%s/channels/%s/messages?v=%f", ts.URL, chanID, v),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f", ts.URL, domainID, chanID, v),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -751,7 +763,7 @@ func TestReadAll(t *testing.T) {
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, domainID, chanID, v, readers.EqualKey),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -763,7 +775,7 @@ func TestReadAll(t *testing.T) {
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, domainID, chanID, v+1, readers.LowerThanKey),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -775,7 +787,7 @@ func TestReadAll(t *testing.T) {
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, domainID, chanID, v+1, readers.LowerThanEqualKey),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -787,7 +799,7 @@ func TestReadAll(t *testing.T) {
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, domainID, chanID, v-1, readers.GreaterThanKey),
token: userToken,
status: http.StatusOK,
authResponse: true,
@@ -799,7 +811,7 @@ func TestReadAll(t *testing.T) {
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, domainID, chanID, v-1, readers.GreaterThanEqualKey),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -811,21 +823,21 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with non-float value as user",
url: fmt.Sprintf("%s/channels/%s/messages?v=ab01", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=ab01", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=wrong", ts.URL, domainID, chanID, v-1),
token: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with boolean value as user",
url: fmt.Sprintf("%s/channels/%s/messages?vb=true", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?vb=true", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -837,14 +849,14 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with non-boolean value as user",
url: fmt.Sprintf("%s/channels/%s/messages?vb=yes", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?vb=yes", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with string value as user",
url: fmt.Sprintf("%s/channels/%s/messages?vs=%s", ts.URL, chanID, vs),
url: fmt.Sprintf("%s/%s/channels/%s/messages?vs=%s", ts.URL, domainID, chanID, vs),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -856,7 +868,7 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with data value as user",
url: fmt.Sprintf("%s/channels/%s/messages?vd=%s", ts.URL, chanID, vd),
url: fmt.Sprintf("%s/%s/channels/%s/messages?vd=%s", ts.URL, domainID, chanID, vd),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -868,21 +880,21 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with non-float from as user",
url: fmt.Sprintf("%s/channels/%s/messages?from=ABCD", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?from=ABCD", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with non-float to as user",
url: fmt.Sprintf("%s/channels/%s/messages?to=ABCD", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?to=ABCD", ts.URL, domainID, chanID),
token: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
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),
url: fmt.Sprintf("%s/%s/channels/%s/messages?from=%f&to=%f", ts.URL, domainID, chanID, messages[19].Time, messages[4].Time),
token: userToken,
authResponse: true,
status: http.StatusOK,
@@ -894,14 +906,14 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with aggregation as user",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=MAX", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX", ts.URL, domainID, chanID),
key: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with interval as user",
url: fmt.Sprintf("%s/channels/%s/messages?interval=10h", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?interval=10h", ts.URL, domainID, chanID),
key: userToken,
authResponse: true,
status: http.StatusOK,
@@ -913,14 +925,14 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with aggregation and interval as user",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=MAX&interval=10h", ts.URL, chanID),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h", ts.URL, domainID, chanID),
key: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval, to and from as user",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=MAX&interval=10h&from=%f&to=%f", ts.URL, chanID, messages[19].Time, messages[4].Time),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&from=%f&to=%f", ts.URL, domainID, chanID, messages[19].Time, messages[4].Time),
key: userToken,
authResponse: true,
status: http.StatusOK,
@@ -932,35 +944,35 @@ func TestReadAll(t *testing.T) {
},
{
desc: "read page with invalid aggregation and valid interval, to and from as user",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=invalid&interval=10h&from=%f&to=%f", ts.URL, chanID, messages[19].Time, messages[4].Time),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=invalid&interval=10h&from=%f&to=%f", ts.URL, domainID, chanID, messages[19].Time, messages[4].Time),
key: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with invalid interval and valid aggregation, to and from as user",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=MAX&interval=10hrs&from=%f&to=%f", ts.URL, chanID, messages[19].Time, messages[4].Time),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10hrs&from=%f&to=%f", ts.URL, domainID, chanID, messages[19].Time, messages[4].Time),
key: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval and to with missing from as user",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=MAX&interval=10h&to=%f", ts.URL, chanID, messages[4].Time),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&to=%f", ts.URL, domainID, chanID, messages[4].Time),
key: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval and to with invalid from as user",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=MAX&interval=10h&to=ABCD&from=%f", ts.URL, chanID, messages[4].Time),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&to=ABCD&from=%f", ts.URL, domainID, chanID, messages[4].Time),
key: userToken,
authResponse: true,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval and to with invalid to as user",
url: fmt.Sprintf("%s/channels/%s/messages?aggregation=MAX&interval=10h&from=%f&to=ABCD", ts.URL, chanID, messages[4].Time),
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&from=%f&to=ABCD", ts.URL, domainID, chanID, messages[4].Time),
key: userToken,
authResponse: true,
status: http.StatusBadRequest,
@@ -969,6 +981,7 @@ func TestReadAll(t *testing.T) {
for _, tc := range cases {
authCall := authz.On("Authorize", mock.Anything, mock.Anything).Return(tc.err)
authCall1 := authn.On("Authenticate", mock.Anything, tc.token).Return(validSession, tc.authnErr)
repo.On("ReadAll", chanID, tc.res.PageMetadata).Return(readers.MessagesPage{Total: tc.res.Total, Messages: fromSenml(tc.res.Messages)}, nil)
if tc.key != "" {
authCall = things.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.ThingsAuthzRes{Authorized: tc.authResponse}, tc.err)
@@ -992,6 +1005,7 @@ func TestReadAll(t *testing.T) {
assert.Equal(t, tc.res.Total, page.Total, fmt.Sprintf("%s: expected %d got %d", tc.desc, tc.res.Total, page.Total))
assert.ElementsMatch(t, tc.res.Messages, page.Messages, fmt.Sprintf("%s: got incorrect body from response", tc.desc))
authCall.Unset()
authCall1.Unset()
}
}
+4
View File
@@ -20,6 +20,7 @@ type listMessagesReq struct {
chanID string
token string
key string
domainID string
pageMeta readers.PageMetadata
}
@@ -27,6 +28,9 @@ func (req listMessagesReq) validate() error {
if req.token == "" && req.key == "" {
return apiutil.ErrBearerToken
}
if req.token != "" && req.domainID == "" {
return apiutil.ErrMissingDomainID
}
if req.chanID == "" {
return apiutil.ErrMissingID
+23 -21
View File
@@ -10,9 +10,11 @@ import (
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/pkg/apiutil"
mgauthn "github.com/absmach/magistrala/pkg/authn"
mgauthz "github.com/absmach/magistrala/pkg/authz"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
"github.com/absmach/magistrala/pkg/policies"
"github.com/absmach/magistrala/readers"
"github.com/go-chi/chi/v5"
kithttp "github.com/go-kit/kit/transport/http"
@@ -43,26 +45,19 @@ const (
defLimit = 10
defOffset = 0
defFormat = "messages"
tokenKind = "token"
thingType = "thing"
userType = "user"
subscribePermission = "subscribe"
viewPermission = "view"
groupType = "group"
)
var errUserAccess = errors.New("user has no permission")
// MakeHandler returns a HTTP handler for API endpoints.
func MakeHandler(svc readers.MessageRepository, authz mgauthz.Authorization, things magistrala.ThingsServiceClient, svcName, instanceID string) http.Handler {
func MakeHandler(svc readers.MessageRepository, authn mgauthn.Authentication, authz mgauthz.Authorization, things magistrala.ThingsServiceClient, svcName, instanceID string) http.Handler {
opts := []kithttp.ServerOption{
kithttp.ServerErrorEncoder(encodeError),
}
mux := chi.NewRouter()
mux.Get("/channels/{chanID}/messages", kithttp.NewServer(
listMessagesEndpoint(svc, authz, things),
mux.Get("/{domainID}/channels/{chanID}/messages", kithttp.NewServer(
listMessagesEndpoint(svc, authn, authz, things),
decodeList,
encodeResponse,
opts...,
@@ -159,9 +154,10 @@ func decodeList(_ context.Context, r *http.Request) (interface{}, error) {
}
req := listMessagesReq{
chanID: chi.URLParam(r, "chanID"),
token: apiutil.ExtractBearerToken(r),
key: apiutil.ExtractThingKey(r),
chanID: chi.URLParam(r, "chanID"),
token: apiutil.ExtractBearerToken(r),
key: apiutil.ExtractThingKey(r),
domainID: chi.URLParam(r, "domainID"),
pageMeta: readers.PageMetadata{
Offset: offset,
Limit: limit,
@@ -219,7 +215,8 @@ func encodeError(_ context.Context, err error, w http.ResponseWriter) {
errors.Contains(err, apiutil.ErrInvalidAggregation),
errors.Contains(err, apiutil.ErrInvalidInterval),
errors.Contains(err, apiutil.ErrMissingFrom),
errors.Contains(err, apiutil.ErrMissingTo):
errors.Contains(err, apiutil.ErrMissingTo),
errors.Contains(err, apiutil.ErrMissingDomainID):
w.WriteHeader(http.StatusBadRequest)
case errors.Contains(err, svcerr.ErrAuthentication),
errors.Contains(err, svcerr.ErrAuthorization),
@@ -242,15 +239,20 @@ func encodeError(_ context.Context, err error, w http.ResponseWriter) {
}
}
func authorize(ctx context.Context, req listMessagesReq, authz mgauthz.Authorization, things magistrala.ThingsServiceClient) (err error) {
func authorize(ctx context.Context, req listMessagesReq, authn mgauthn.Authentication, authz mgauthz.Authorization, things magistrala.ThingsServiceClient) (err error) {
switch {
case req.token != "":
session, err := authn.Authenticate(ctx, req.token)
if err != nil {
return errors.Wrap(svcerr.ErrAuthentication, err)
}
if err = authz.Authorize(ctx, mgauthz.PolicyReq{
SubjectType: userType,
SubjectKind: tokenKind,
Subject: req.token,
Permission: viewPermission,
ObjectType: groupType,
Domain: req.domainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: req.domainID + "_" + session.UserID,
Permission: policies.ViewPermission,
ObjectType: policies.GroupType,
Object: req.chanID,
}); err != nil {
e, ok := status.FromError(err)
@@ -264,7 +266,7 @@ func authorize(ctx context.Context, req listMessagesReq, authz mgauthz.Authoriza
if _, err = things.Authorize(ctx, &magistrala.ThingsAuthzReq{
ThingKey: req.key,
ChannelID: req.chanID,
Permission: subscribePermission,
Permission: policies.SubscribePermission,
}); err != nil {
e, ok := status.FromError(err)
if ok && e.Code() == codes.PermissionDenied {