MF-1567 - Use Bearer, Thing or Basic scheme in Authorization header (#1568)

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>
This commit is contained in:
Manuel Imperiale 2022-03-06 01:49:34 +01:00 committed by GitHub
parent e5278c463f
commit 0a6b2f135a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 234 additions and 200 deletions

View File

@ -55,7 +55,7 @@ func updateCertEndpoint(svc bootstrap.Service) endpoint.Endpoint {
return nil, err
}
if err := svc.UpdateCert(ctx, req.key, req.thingID, req.ClientCert, req.ClientKey, req.CACert); err != nil {
if err := svc.UpdateCert(ctx, req.token, req.thingID, req.ClientCert, req.ClientKey, req.CACert); err != nil {
return nil, err
}
@ -73,7 +73,7 @@ func viewEndpoint(svc bootstrap.Service) endpoint.Endpoint {
return nil, err
}
config, err := svc.View(ctx, req.key, req.id)
config, err := svc.View(ctx, req.token, req.id)
if err != nil {
return nil, err
}
@ -116,7 +116,7 @@ func updateEndpoint(svc bootstrap.Service) endpoint.Endpoint {
Content: req.Content,
}
if err := svc.Update(ctx, req.key, config); err != nil {
if err := svc.Update(ctx, req.token, config); err != nil {
return nil, err
}
@ -137,7 +137,7 @@ func updateConnEndpoint(svc bootstrap.Service) endpoint.Endpoint {
return nil, err
}
if err := svc.UpdateConnections(ctx, req.key, req.id, req.Channels); err != nil {
if err := svc.UpdateConnections(ctx, req.token, req.id, req.Channels); err != nil {
return nil, err
}
@ -158,7 +158,7 @@ func listEndpoint(svc bootstrap.Service) endpoint.Endpoint {
return nil, err
}
page, err := svc.List(ctx, req.key, req.filter, req.offset, req.limit)
page, err := svc.List(ctx, req.token, req.filter, req.offset, req.limit)
if err != nil {
return nil, err
}
@ -204,7 +204,7 @@ func removeEndpoint(svc bootstrap.Service) endpoint.Endpoint {
return removeRes{}, err
}
if err := svc.Remove(ctx, req.key, req.id); err != nil {
if err := svc.Remove(ctx, req.token, req.id); err != nil {
return nil, err
}
@ -236,7 +236,7 @@ func stateEndpoint(svc bootstrap.Service) endpoint.Endpoint {
return nil, err
}
if err := svc.ChangeState(ctx, req.key, req.id, req.State); err != nil {
if err := svc.ChangeState(ctx, req.token, req.id, req.State); err != nil {
return nil, err
}

View File

@ -99,6 +99,7 @@ type testRequest struct {
url string
contentType string
token string
key string
body io.Reader
}
@ -125,6 +126,9 @@ func (tr testRequest) make() (*http.Response, error) {
if tr.token != "" {
req.Header.Set("Authorization", apiutil.BearerPrefix+tr.token)
}
if tr.key != "" {
req.Header.Set("Authorization", apiutil.ThingPrefix+tr.key)
}
if tr.contentType != "" {
req.Header.Set("Content-Type", tr.contentType)
@ -1147,7 +1151,7 @@ func TestBootstrap(t *testing.T) {
client: bs.Client(),
method: http.MethodGet,
url: fmt.Sprintf("%s/things/bootstrap/%s", bs.URL, tc.externalID),
token: tc.externalKey,
key: tc.externalKey,
}
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))

View File

@ -44,13 +44,13 @@ func (req addReq) validate() error {
}
type entityReq struct {
key string
id string
token string
id string
}
func (req entityReq) validate() error {
if req.key == "" {
return apiutil.ErrBearerKey
if req.token == "" {
return apiutil.ErrBearerToken
}
if req.id == "" {
@ -61,15 +61,15 @@ func (req entityReq) validate() error {
}
type updateReq struct {
key string
token string
id string
Name string `json:"name"`
Content string `json:"content"`
}
func (req updateReq) validate() error {
if req.key == "" {
return apiutil.ErrBearerKey
if req.token == "" {
return apiutil.ErrBearerToken
}
if req.id == "" {
@ -80,7 +80,7 @@ func (req updateReq) validate() error {
}
type updateCertReq struct {
key string
token string
thingID string
ClientCert string `json:"client_cert"`
ClientKey string `json:"client_key"`
@ -88,8 +88,8 @@ type updateCertReq struct {
}
func (req updateCertReq) validate() error {
if req.key == "" {
return apiutil.ErrBearerKey
if req.token == "" {
return apiutil.ErrBearerToken
}
if req.thingID == "" {
@ -100,14 +100,14 @@ func (req updateCertReq) validate() error {
}
type updateConnReq struct {
key string
token string
id string
Channels []string `json:"channels"`
}
func (req updateConnReq) validate() error {
if req.key == "" {
return apiutil.ErrBearerKey
if req.token == "" {
return apiutil.ErrBearerToken
}
if req.id == "" {
@ -118,15 +118,15 @@ func (req updateConnReq) validate() error {
}
type listReq struct {
key string
token string
filter bootstrap.Filter
offset uint64
limit uint64
}
func (req listReq) validate() error {
if req.key == "" {
return apiutil.ErrBearerKey
if req.token == "" {
return apiutil.ErrBearerToken
}
if req.limit > maxLimitSize {
@ -154,14 +154,14 @@ func (req bootstrapReq) validate() error {
}
type changeStateReq struct {
key string
token string
id string
State bootstrap.State `json:"state"`
}
func (req changeStateReq) validate() error {
if req.key == "" {
return apiutil.ErrBearerKey
if req.token == "" {
return apiutil.ErrBearerToken
}
if req.id == "" {

View File

@ -54,28 +54,28 @@ func TestAddReqValidation(t *testing.T) {
func TestEntityReqValidation(t *testing.T) {
cases := []struct {
desc string
key string
id string
err error
desc string
token string
id string
err error
}{
{
desc: "empty key",
key: "",
id: "id",
err: apiutil.ErrBearerKey,
desc: "empty token",
token: "",
id: "id",
err: apiutil.ErrBearerToken,
},
{
desc: "empty id",
key: "key",
id: "",
err: apiutil.ErrMissingID,
desc: "empty id",
token: "token",
id: "",
err: apiutil.ErrMissingID,
},
}
for _, tc := range cases {
req := entityReq{
key: tc.key,
token: tc.token,
}
err := req.validate()
@ -85,29 +85,29 @@ func TestEntityReqValidation(t *testing.T) {
func TestUpdateReqValidation(t *testing.T) {
cases := []struct {
desc string
key string
id string
err error
desc string
token string
id string
err error
}{
{
desc: "empty key",
key: "",
id: "id",
err: apiutil.ErrBearerKey,
desc: "empty token",
token: "",
id: "id",
err: apiutil.ErrBearerToken,
},
{
desc: "empty id",
key: "key",
id: "",
err: apiutil.ErrMissingID,
desc: "empty id",
token: "token",
id: "",
err: apiutil.ErrMissingID,
},
}
for _, tc := range cases {
req := updateReq{
key: tc.key,
id: tc.id,
token: tc.token,
id: tc.id,
}
err := req.validate()
@ -118,19 +118,19 @@ func TestUpdateReqValidation(t *testing.T) {
func TestUpdateCertReqValidation(t *testing.T) {
cases := []struct {
desc string
key string
token string
thingID string
err error
}{
{
desc: "empty key",
key: "",
desc: "empty token",
token: "",
thingID: "thingID",
err: apiutil.ErrBearerKey,
err: apiutil.ErrBearerToken,
},
{
desc: "empty thing id",
key: "key",
token: "token",
thingID: "",
err: apiutil.ErrMissingID,
},
@ -138,7 +138,7 @@ func TestUpdateCertReqValidation(t *testing.T) {
for _, tc := range cases {
req := updateCertReq{
key: tc.key,
token: tc.token,
thingID: tc.thingID,
}
@ -149,29 +149,29 @@ func TestUpdateCertReqValidation(t *testing.T) {
func TestUpdateConnReqValidation(t *testing.T) {
cases := []struct {
desc string
key string
id string
err error
desc string
token string
id string
err error
}{
{
desc: "empty key",
key: "",
id: "id",
err: apiutil.ErrBearerKey,
desc: "empty token",
token: "",
id: "id",
err: apiutil.ErrBearerToken,
},
{
desc: "empty id",
key: "key",
id: "",
err: apiutil.ErrMissingID,
desc: "empty id",
token: "token",
id: "",
err: apiutil.ErrMissingID,
},
}
for _, tc := range cases {
req := updateReq{
key: tc.key,
id: tc.id,
token: tc.token,
id: tc.id,
}
err := req.validate()
@ -183,27 +183,27 @@ func TestListReqValidation(t *testing.T) {
cases := []struct {
desc string
offset uint64
key string
token string
limit uint64
err error
}{
{
desc: "empty key",
key: "",
desc: "empty token",
token: "",
offset: 0,
limit: 1,
err: apiutil.ErrBearerKey,
err: apiutil.ErrBearerToken,
},
{
desc: "too large limit",
key: "key",
token: "token",
offset: 0,
limit: maxLimitSize + 1,
err: apiutil.ErrLimitSize,
},
{
desc: "default limit",
key: "key",
token: "token",
offset: 0,
limit: defLimit,
err: nil,
@ -212,7 +212,7 @@ func TestListReqValidation(t *testing.T) {
for _, tc := range cases {
req := listReq{
key: tc.key,
token: tc.token,
offset: tc.offset,
limit: tc.limit,
}
@ -257,28 +257,28 @@ func TestBootstrapReqValidation(t *testing.T) {
func TestChangeStateReqValidation(t *testing.T) {
cases := []struct {
desc string
key string
token string
id string
state bootstrap.State
err error
}{
{
desc: "empty key",
key: "",
desc: "empty token",
token: "",
id: "id",
state: bootstrap.State(1),
err: apiutil.ErrBearerKey,
err: apiutil.ErrBearerToken,
},
{
desc: "empty id",
key: "key",
token: "token",
id: "",
state: bootstrap.State(0),
err: apiutil.ErrMissingID,
},
{
desc: "invalid state",
key: "key",
token: "token",
id: "id",
state: bootstrap.State(14),
err: apiutil.ErrBootstrapState,
@ -287,7 +287,7 @@ func TestChangeStateReqValidation(t *testing.T) {
for _, tc := range cases {
req := changeStateReq{
key: tc.key,
token: tc.token,
id: tc.id,
State: tc.state,
}

View File

@ -125,8 +125,8 @@ func decodeUpdateRequest(_ context.Context, r *http.Request) (interface{}, error
}
req := updateReq{
key: apiutil.ExtractBearerToken(r),
id: bone.GetValue(r, "id"),
token: apiutil.ExtractBearerToken(r),
id: bone.GetValue(r, "id"),
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(errors.ErrMalformedEntity, err)
@ -141,7 +141,7 @@ func decodeUpdateCertRequest(_ context.Context, r *http.Request) (interface{}, e
}
req := updateCertReq{
key: apiutil.ExtractBearerToken(r),
token: apiutil.ExtractBearerToken(r),
thingID: bone.GetValue(r, "id"),
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@ -157,8 +157,8 @@ func decodeUpdateConnRequest(_ context.Context, r *http.Request) (interface{}, e
}
req := updateConnReq{
key: apiutil.ExtractBearerToken(r),
id: bone.GetValue(r, "id"),
token: apiutil.ExtractBearerToken(r),
id: bone.GetValue(r, "id"),
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(errors.ErrMalformedEntity, err)
@ -184,7 +184,7 @@ func decodeListRequest(_ context.Context, r *http.Request) (interface{}, error)
}
req := listReq{
key: apiutil.ExtractBearerToken(r),
token: apiutil.ExtractBearerToken(r),
filter: parseFilter(q),
offset: o,
limit: l,
@ -196,7 +196,7 @@ func decodeListRequest(_ context.Context, r *http.Request) (interface{}, error)
func decodeBootstrapRequest(_ context.Context, r *http.Request) (interface{}, error) {
req := bootstrapReq{
id: bone.GetValue(r, "external_id"),
key: apiutil.ExtractBearerToken(r),
key: apiutil.ExtractThingKey(r),
}
return req, nil
@ -208,8 +208,8 @@ func decodeStateRequest(_ context.Context, r *http.Request) (interface{}, error)
}
req := changeStateReq{
key: apiutil.ExtractBearerToken(r),
id: bone.GetValue(r, "id"),
token: apiutil.ExtractBearerToken(r),
id: bone.GetValue(r, "id"),
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(errors.ErrMalformedEntity, err)
@ -220,8 +220,8 @@ func decodeStateRequest(_ context.Context, r *http.Request) (interface{}, error)
func decodeEntityRequest(_ context.Context, r *http.Request) (interface{}, error) {
req := entityReq{
key: apiutil.ExtractBearerToken(r),
id: bone.GetValue(r, "id"),
token: apiutil.ExtractBearerToken(r),
id: bone.GetValue(r, "id"),
}
return req, nil

View File

@ -42,7 +42,7 @@ func (req *listReq) validate() error {
if req.token == "" {
return apiutil.ErrBearerToken
}
if req.limit > 1 || req.limit > maxLimitSize {
if req.limit > maxLimitSize {
return apiutil.ErrLimitSize
}
return nil

View File

@ -50,7 +50,7 @@ func (tr testRequest) make() (*http.Response, error) {
}
if tr.token != "" {
req.Header.Set("Authorization", apiutil.BearerPrefix+tr.token)
req.Header.Set("Authorization", apiutil.ThingPrefix+tr.token)
}
if tr.basicAuth && tr.token != "" {
req.SetBasicAuth("", tr.token)

View File

@ -116,7 +116,7 @@ func decodeRequest(ctx context.Context, r *http.Request) (interface{}, error) {
case ok:
token = pass
case !ok:
token = apiutil.ExtractBearerToken(r)
token = apiutil.ExtractThingKey(r)
}
payload, err := ioutil.ReadAll(r.Body)

View File

@ -11,6 +11,9 @@ import (
// BearerPrefix represents the token prefix for Bearer authentication scheme.
const BearerPrefix = "Bearer "
// ThingPrefix represents the key prefix for Thing authentication scheme.
const ThingPrefix = "Thing "
// ExtractBearerToken returns value of the bearer token. If there is no bearer token - an empty value is returned.
func ExtractBearerToken(r *http.Request) string {
token := r.Header.Get("Authorization")
@ -21,3 +24,14 @@ func ExtractBearerToken(r *http.Request) string {
return strings.TrimPrefix(token, BearerPrefix)
}
// ExtractThingKey returns value of the thing key. If there is no thing key - an empty value is returned.
func ExtractThingKey(r *http.Request) string {
token := r.Header.Get("Authorization")
if !strings.HasPrefix(token, ThingPrefix) {
return ""
}
return strings.TrimPrefix(token, ThingPrefix)
}

View File

@ -13,7 +13,7 @@ import (
"github.com/mainflux/mainflux/pkg/errors"
)
func (sdk mfSDK) SendMessage(chanName, msg, token string) error {
func (sdk mfSDK) SendMessage(chanName, msg, key string) error {
chanNameParts := strings.SplitN(chanName, ".", 2)
chanID := chanNameParts[0]
subtopicPart := ""
@ -28,7 +28,7 @@ func (sdk mfSDK) SendMessage(chanName, msg, token string) error {
return err
}
resp, err := sdk.sendRequest(req, token, string(sdk.msgContentType))
resp, err := sdk.sendThingRequest(req, key, string(sdk.msgContentType))
if err != nil {
return err
}

View File

@ -337,3 +337,15 @@ func (sdk mfSDK) sendRequest(req *http.Request, token, contentType string) (*htt
return sdk.client.Do(req)
}
func (sdk mfSDK) sendThingRequest(req *http.Request, key, contentType string) (*http.Response, error) {
if key != "" {
req.Header.Set("Authorization", apiutil.ThingPrefix+key)
}
if contentType != "" {
req.Header.Add("Content-Type", contentType)
}
return sdk.client.Do(req)
}

View File

@ -13,6 +13,7 @@ import (
"time"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/internal/apiutil"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/transformers/senml"
"github.com/mainflux/mainflux/pkg/uuid"
@ -59,6 +60,7 @@ type testRequest struct {
method string
url string
token string
key string
body io.Reader
}
@ -68,7 +70,10 @@ func (tr testRequest) make() (*http.Response, error) {
return nil, err
}
if tr.token != "" {
req.Header.Set("Authorization", tr.token)
req.Header.Set("Authorization", apiutil.BearerPrefix+tr.token)
}
if tr.key != "" {
req.Header.Set("Authorization", apiutil.ThingPrefix+tr.key)
}
return tr.client.Do(req)
@ -141,13 +146,14 @@ func TestReadAll(t *testing.T) {
req string
url string
token string
key string
status int
res pageRes
}{
{
desc: "read page with valid offset and limit",
url: fmt.Sprintf("%s/channels/%s/messages?offset=0&limit=10", ts.URL, chanID),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(messages)),
@ -157,7 +163,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(messages)),
@ -167,55 +173,55 @@ func TestReadAll(t *testing.T) {
{
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),
key: thingToken,
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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusBadRequest,
},
{
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),
key: thingToken,
status: http.StatusBadRequest,
},
{
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),
token: invalid,
status: http.StatusUnauthorized,
},
{
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),
key: thingToken,
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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusBadRequest,
},
{
@ -227,7 +233,7 @@ func TestReadAll(t *testing.T) {
{
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),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(messages)),
@ -237,7 +243,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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(messages)),
@ -247,7 +253,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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(messages)),
@ -257,7 +263,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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(queryMsgs)),
@ -267,7 +273,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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(queryMsgs)),
@ -277,7 +283,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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(queryMsgs)),
@ -287,7 +293,7 @@ func TestReadAll(t *testing.T) {
{
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),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(queryMsgs)),
@ -297,7 +303,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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(queryMsgs)),
@ -307,7 +313,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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(valueMsgs)),
@ -317,7 +323,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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(valueMsgs)),
@ -327,7 +333,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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(valueMsgs)),
@ -337,7 +343,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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(valueMsgs)),
@ -347,7 +353,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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(valueMsgs)),
@ -357,7 +363,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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(valueMsgs)),
@ -367,19 +373,19 @@ 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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusBadRequest,
},
{
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),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(boolMsgs)),
@ -389,13 +395,13 @@ 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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusBadRequest,
},
{
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),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(stringMsgs)),
@ -405,7 +411,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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(dataMsgs)),
@ -415,20 +421,20 @@ 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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusBadRequest,
},
{
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),
key: thingToken,
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),
token: fmt.Sprintf("Thing %s", thingToken),
key: thingToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(messages[5:20])),
@ -438,7 +444,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(messages)),
@ -448,55 +454,55 @@ 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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusBadRequest,
},
{
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),
token: userToken,
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),
token: fmt.Sprintf("Bearer %s", invalid),
token: invalid,
status: http.StatusUnauthorized,
},
{
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),
token: userToken,
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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusBadRequest,
},
{
@ -508,7 +514,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(messages)),
@ -518,7 +524,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(messages)),
@ -528,7 +534,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(messages)),
@ -538,7 +544,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(queryMsgs)),
@ -548,7 +554,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(queryMsgs)),
@ -558,7 +564,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(queryMsgs)),
@ -568,7 +574,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(queryMsgs)),
@ -578,7 +584,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(queryMsgs)),
@ -588,7 +594,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(valueMsgs)),
@ -598,7 +604,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(valueMsgs)),
@ -608,7 +614,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(valueMsgs)),
@ -618,7 +624,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(valueMsgs)),
@ -628,7 +634,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(valueMsgs)),
@ -638,7 +644,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(valueMsgs)),
@ -648,19 +654,19 @@ 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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusBadRequest,
},
{
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),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(boolMsgs)),
@ -670,13 +676,13 @@ 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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusBadRequest,
},
{
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),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(stringMsgs)),
@ -686,7 +692,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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(dataMsgs)),
@ -696,20 +702,20 @@ 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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusBadRequest,
},
{
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),
token: userToken,
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),
token: fmt.Sprintf("Bearer %s", userToken),
token: userToken,
status: http.StatusOK,
res: pageRes{
Total: uint64(len(messages[5:20])),
@ -724,6 +730,7 @@ func TestReadAll(t *testing.T) {
method: http.MethodGet,
url: tc.url,
token: tc.token,
key: tc.key,
}
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))

View File

@ -17,11 +17,12 @@ type apiReq interface {
type listMessagesReq struct {
chanID string
token string
key string
pageMeta readers.PageMetadata
}
func (req listMessagesReq) validate() error {
if req.token == "" {
if req.token == "" && req.key == "" {
return apiutil.ErrBearerToken
}

View File

@ -7,7 +7,6 @@ import (
"context"
"encoding/json"
"net/http"
"strings"
kithttp "github.com/go-kit/kit/transport/http"
"github.com/go-zoo/bone"
@ -22,26 +21,24 @@ 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"
boolValueKey = "vb"
comparatorKey = "comparator"
fromKey = "from"
toKey = "to"
defLimit = 10
defOffset = 0
defFormat = "messages"
thingTokenPrefix = "Thing "
userTokenPrefix = "Bearer "
contentType = "application/json"
offsetKey = "offset"
limitKey = "limit"
formatKey = "format"
subtopicKey = "subtopic"
publisherKey = "publisher"
protocolKey = "protocol"
nameKey = "name"
valueKey = "v"
stringValueKey = "vs"
dataValueKey = "vd"
boolValueKey = "vb"
comparatorKey = "comparator"
fromKey = "from"
toKey = "to"
defLimit = 10
defOffset = 0
defFormat = "messages"
)
var (
@ -142,7 +139,8 @@ func decodeList(ctx context.Context, r *http.Request) (interface{}, error) {
req := listMessagesReq{
chanID: bone.GetValue(r, "chanID"),
token: r.Header.Get("Authorization"),
token: apiutil.ExtractBearerToken(r),
key: apiutil.ExtractThingKey(r),
pageMeta: readers.PageMetadata{
Offset: offset,
Limit: limit,
@ -219,9 +217,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, userTokenPrefix):
token := strings.TrimPrefix(req.token, userTokenPrefix)
user, err := usersAuth.Identify(ctx, &mainflux.Token{Value: token})
case req.token != "":
user, err := usersAuth.Identify(ctx, &mainflux.Token{Value: req.token})
if err != nil {
e, ok := status.FromError(err)
if ok && e.Code() == codes.PermissionDenied {
@ -238,8 +235,7 @@ func authorize(ctx context.Context, req listMessagesReq, tc mainflux.ThingsServi
}
return nil
default:
token := strings.TrimPrefix(req.token, thingTokenPrefix)
if _, err := thingsAuth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: token, ChanID: req.chanID}); err != nil {
if _, err := thingsAuth.CanAccessByKey(ctx, &mainflux.AccessByKeyReq{Token: req.key, ChanID: req.chanID}); err != nil {
return errors.Wrap(errThingAccess, err)
}
return nil