MF-1439 - Add support for Basic Authentication in HTTP Adapter (#1441)

* Add support for basic auth for HTTP adapter

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Fix API docs

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Inline BasicAuth check

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>
This commit is contained in:
Dušan Borovčanin 2021-07-22 15:47:55 +02:00 committed by GitHub
parent 5ac1203b55
commit d6a3830ef4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 33 deletions

View File

@ -6,6 +6,9 @@ info:
paths:
/channels/{id}/messages:
post:
security:
- jwtAuth: []
- basicAuth: []
summary: Sends message to the communication channel
description: |
Sends message to the communication channel. Messages can be sent as
@ -13,22 +16,21 @@ paths:
tags:
- messages
parameters:
- $ref: "#/components/parameters/Authorization"
- $ref: "#/components/parameters/ID"
requestBody:
$ref: "#/components/requestBodies/MessageReq"
responses:
'202':
"202":
description: Message is accepted for processing.
'400':
"400":
description: Message discarded due to its malformed content.
'403':
"403":
description: Message discarded due to missing or invalid credentials.
'404':
"404":
description: Message discarded due to invalid channel id.
'415':
"415":
description: Message discarded due to invalid or missing content type.
'500':
"500":
description: Unexpected server-side error occurred.
components:
@ -94,16 +96,15 @@ components:
type: array
items:
$ref: "#/components/schemas/SenMLRecord"
parameters:
Authorization:
name: Authorization
description: Access token.
securitySchemes:
basicAuth:
type: http
scheme: basic
jwtAuth:
type: apiKey
in: header
schema:
type: string
format: jwt
required: true
name: Authorization
parameters:
ID:
name: id
description: Unique channel identifier.
@ -116,11 +117,11 @@ components:
requestBodies:
MessageReq:
description: |
Message to be distributed. Since the platform expects messages to be
properly formatted SenML in order to be post-processed, clients are
obliged to specify Content-Type header for each published message.
Note that all messages that aren't SenML will be accepted and published,
but no post-processing will be applied.
Message to be distributed. Since the platform expects messages to be
properly formatted SenML in order to be post-processed, clients are
obliged to specify Content-Type header for each published message.
Note that all messages that aren't SenML will be accepted and published,
but no post-processing will be applied.
required: true
content:
application/json:

View File

@ -8,16 +8,16 @@ 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_HTTP_ADAPTER_LOG_LEVEL | Log level for the HTTP Adapter | error |
| MF_HTTP_ADAPTER_PORT | Service HTTP port | 8180 |
| MF_NATS_URL | NATS instance URL | nats://localhost:4222 |
| MF_HTTP_ADAPTER_CLIENT_TLS | Flag that indicates if TLS should be turned on | false |
| MF_HTTP_ADAPTER_CA_CERTS | Path to trusted CAs 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_TIMEOUT | Things service Auth gRPC request timeout in seconds | 1s |
| Variable | Description | Default |
| --------------------------- | --------------------------------------------------- | --------------------- |
| MF_HTTP_ADAPTER_LOG_LEVEL | Log level for the HTTP Adapter | error |
| MF_HTTP_ADAPTER_PORT | Service HTTP port | 8180 |
| MF_NATS_URL | NATS instance URL | nats://localhost:4222 |
| MF_HTTP_ADAPTER_CLIENT_TLS | Flag that indicates if TLS should be turned on | false |
| MF_HTTP_ADAPTER_CA_CERTS | Path to trusted CAs 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_TIMEOUT | Things service Auth gRPC request timeout in seconds | 1s |
## Deployment
@ -53,7 +53,9 @@ Setting `MF_HTTP_ADAPTER_CA_CERTS` expects a file in PEM format of trusted CAs.
## Usage
HTTP Authorization request header contains the credentials to authenticate a Thing. The authorization header can be a plain Thing key
or a Thing key encoded as a password for Basic Authentication. In case the Basic Authentication schema is used, the username is ignored.
For more information about service capabilities and its usage, please check out
the [API documentation](https://api.mainflux.io/?urls.primaryName=http-openapi.yml).
the [API documentation](https://api.mainflux.io/?urls.primaryName=http.yml).
[doc]: https://docs.mainflux.io

View File

@ -37,6 +37,7 @@ type testRequest struct {
contentType string
token string
body io.Reader
basicAuth bool
}
func (tr testRequest) make() (*http.Response, error) {
@ -44,9 +45,13 @@ func (tr testRequest) make() (*http.Response, error) {
if err != nil {
return nil, err
}
if tr.token != "" {
req.Header.Set("Authorization", tr.token)
}
if tr.basicAuth && tr.token != "" {
req.SetBasicAuth("", tr.token)
}
if tr.contentType != "" {
req.Header.Set("Content-Type", tr.contentType)
}
@ -70,6 +75,7 @@ func TestPublish(t *testing.T) {
contentType string
auth string
status int
basicAuth bool
}{
"publish message": {
chanID: chanID,
@ -85,6 +91,14 @@ func TestPublish(t *testing.T) {
auth: "",
status: http.StatusForbidden,
},
"publish message with basic auth": {
chanID: chanID,
msg: msg,
contentType: contentType,
auth: token,
basicAuth: true,
status: http.StatusAccepted,
},
"publish message with invalid authorization token": {
chanID: chanID,
msg: msg,
@ -92,6 +106,14 @@ func TestPublish(t *testing.T) {
auth: invalidToken,
status: http.StatusForbidden,
},
"publish message with invalid basic auth": {
chanID: chanID,
msg: msg,
contentType: contentType,
auth: invalidToken,
basicAuth: true,
status: http.StatusForbidden,
},
"publish message without content type": {
chanID: chanID,
msg: msg,
@ -123,6 +145,7 @@ func TestPublish(t *testing.T) {
contentType: tc.contentType,
token: tc.auth,
body: strings.NewReader(tc.msg),
basicAuth: tc.basicAuth,
}
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", desc, err))

View File

@ -103,6 +103,10 @@ func decodeRequest(ctx context.Context, r *http.Request) (interface{}, error) {
if err != nil {
return nil, err
}
token := r.Header.Get("Authorization")
if _, pass, ok := r.BasicAuth(); ok {
token = pass
}
payload, err := decodePayload(r.Body)
if err != nil {
@ -119,7 +123,7 @@ func decodeRequest(ctx context.Context, r *http.Request) (interface{}, error) {
req := publishReq{
msg: msg,
token: r.Header.Get("Authorization"),
token: token,
}
return req, nil