Skip to content

Commit

Permalink
Update Flask, Waitress
Browse files Browse the repository at this point in the history
Use now flask-restx and no more flask-restplus
Add multiple authentication methode
  • Loading branch information
Fizcko committed Sep 14, 2021
1 parent e4ac82e commit 21b4442
Show file tree
Hide file tree
Showing 17 changed files with 123 additions and 56 deletions.
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ ENV JWT_SECRET "Change-this-secret-phrase"
ENV JWT_ALGORITHM "HS256"
ENV JWT_ACCESS_TOKEN_EXPIRES "900"

ENV API_SECURITY "None"
ENV API_USERNAME "admin"
ENV API_PASSWORD "admin"

Expand All @@ -35,7 +36,7 @@ RUN apt-get update \
gammu=1.40.0-1 \
gammu-smsd=1.40.0-1 \
libgammu-dev=1.40.0-1 \
libmariadb-dev=1:10.3.22-0+deb10u1 \
libmariadb-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

Expand Down
9 changes: 7 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This REST API allow you to send and receive SMS using gammu-smsd.

A 2.0 swagger documentation is provided at the root URL.

All routes are protected by a Bearer authentication.
All routes can be protected by an authentication methode (Basic, Bearer).

# Table of Contents

Expand Down Expand Up @@ -129,6 +129,11 @@ $ docker-compose up -d
default value : "900"
description : How long (in ms) an access token should live before it expires. Can be set to 0 to disable expiration.

#### API_SECURITY
default value : "None"
allowed values : "None" | "Bearer" | "Basic"
description : Select the authentication methode for the routes of the Rest API

#### API_USERNAME
default value : "admin"
description : User name used for connection to the rest API
Expand All @@ -150,4 +155,4 @@ $ docker-compose up -d

# Screenshots

![Swagger](screenshots/swagger.png)
![Swagger](screenshots/swagger.png)
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: '3.3'
services:
backend:
image: fizcko/sms-gateway
image: fizcko/sms-gateway:latest
restart: always
ports:
- 5000:5000
Expand Down
19 changes: 17 additions & 2 deletions src/environment/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,26 @@
# Environment config
port = os.environ.get("SERVER_PORT", "5000")
ip = os.environ.get("SERVER_IP", "0.0.0.0")
security = os.environ.get("API_SECURITY", "None")

if security == 'Bearer':
require_bearer = True
require_basic = False
elif security == 'Basic':
require_bearer = False
require_basic = True
else:
security = []
require_bearer = False
require_basic = False

environment_config = {
"ip": ip,
"port": port,
"swagger-url": "/"
"swagger-url": "/",
"security": security,
"require_bearer": require_bearer,
"require_basic": require_basic
}

# JWT config
Expand Down Expand Up @@ -39,4 +54,4 @@

gammu_smsd_config = {
"conf": gammu_smsd_conf
}
}
2 changes: 1 addition & 1 deletion src/models/auth.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from flask_restplus import fields
from flask_restx import fields
from server.instance import server

login_post = server.api.model('Login Payload', {
Expand Down
4 changes: 2 additions & 2 deletions src/models/inbox.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from flask_restplus import fields
from flask_restx import fields
from server.instance import server

inbox_item = server.api.model('inbox item', {
Expand Down Expand Up @@ -53,4 +53,4 @@
'results': fields.List(
fields.Nested(inbox_item)
)
})
})
4 changes: 2 additions & 2 deletions src/models/outbox.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from flask_restplus import fields
from flask_restx import fields
from server.instance import server
from datetime import time

Expand Down Expand Up @@ -85,4 +85,4 @@ def format(self, value):
'results': fields.List(
fields.Nested(outbox_item)
)
})
})
4 changes: 2 additions & 2 deletions src/models/send.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from flask_restplus import fields
from flask_restx import fields
from server.instance import server

sms_post = server.api.model('Send SMS Payload', {
Expand Down Expand Up @@ -40,4 +40,4 @@
]

)
})
})
4 changes: 2 additions & 2 deletions src/models/senditems.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from flask_restplus import fields
from flask_restx import fields
from server.instance import server

senditems_item = server.api.model('senditems item', {
Expand Down Expand Up @@ -74,4 +74,4 @@
'results': fields.List(
fields.Nested(senditems_item)
)
})
})
12 changes: 5 additions & 7 deletions src/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
waitress==1.4.3
flask==1.1.2
flask-restplus==0.13.0
flask-jwt-extended==3.24.1
waitress==2.0.0
flask==2.0.1
flask-restx==0.5.1
flask-jwt-extended==4.3.0
flask-basicauth==0.2.0
SQLAlchemy==1.3.17
SQLAlchemy-Utils==0.36.6
python-gammu==2.12
mysqlclient==1.4.6

# Fix bug in flask
werkzeug==0.16.1
25 changes: 25 additions & 0 deletions src/resources/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from server.instance import server
from flask_jwt_extended import ( verify_jwt_in_request )
from flask_basicauth import BasicAuth

app = server.app
basic_auth = BasicAuth(app)

def required_bearerAuth(bearerAuth = True):
def decorator(fn):
def decorated(*args,**kwargs):
if bearerAuth:
verify_jwt_in_request()
return fn(*args,**kwargs)
return decorated
return decorator

def required_basicAuth(basicAuth = True):
def decorator(fn):
def decorated(*args,**kwargs):
if basicAuth:
if not basic_auth.authenticate():
return {'message': 'Basic authenfication fail', 'details': 'Wrong username or password.'}, 401
return fn(*args,**kwargs)
return decorated
return decorator
13 changes: 8 additions & 5 deletions src/resources/v1/auth.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from flask import request
from flask_restplus import Resource
from flask_restx import Resource
from flask_jwt_extended import (create_access_token, get_jwt_identity, jwt_required)
from resources.decorators import ( required_bearerAuth )
from environment.instance import environment_config

import os

from server.instance import server
Expand All @@ -13,8 +16,9 @@
class login(Resource):

@ns.expect(login_post, validate=True)
@ns.doc(description='Get a token for requests')
@ns.doc(description='Get a bearer token for requests protected by a bearer Authentication')
@api.response(200, 'Success', token_response)
@api.doc(security=[])
def post(self):
''' Get a token for requests'''
json_data = request.json
Expand All @@ -33,10 +37,9 @@ def post(self):
@ns.route('/v1/refresh')
class refresh(Resource):

@ns.doc(description='Get a new token for requests')
@ns.doc(description='Get a new bearer token for requests protected by a bearer Authentication')
@api.response(200, 'Success', token_response)
@api.doc(security='Bearer')
@jwt_required
@required_bearerAuth(environment_config["require_bearer"])
def get(self):
''' Get a token for requests'''
current_user = get_jwt_identity()
Expand Down
18 changes: 10 additions & 8 deletions src/resources/v1/inbox.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from flask import request
from flask_restplus import Resource, reqparse
from flask_restx import Resource, reqparse
from flask_jwt_extended import ( jwt_required )
from database.models import inbox
from database.instance import db_session
from server.instance import server
from math import ceil
from models.inbox import inbox_item, inbox_items
from resources.decorators import ( required_bearerAuth, required_basicAuth )
from environment.instance import environment_config

api = server.api
ns = api.namespace('Inbox', description='Inbox operations', path='/')
Expand All @@ -22,8 +24,8 @@ class Inbox(Resource):
@api.doc(params={'after': {'description': 'Filter SMS received after a date', 'in': 'query', 'type': 'date'}})
@api.doc(params={'sender': {'description': 'Filter SMS received from a number', 'in': 'query', 'type': 'string'}})
@api.expect(pagination_arguments, validate=True)
@api.doc(security='Bearer')
@jwt_required
@required_bearerAuth(environment_config["require_bearer"])
@required_basicAuth(environment_config["require_basic"])
@api.response(200, 'Success', inbox_items)
def get(self):
''' Get all SMS located in the inbox'''
Expand Down Expand Up @@ -66,8 +68,8 @@ class InboxID(Resource):

@ns.doc(params={'id': 'ID of a SMS to get'})
@ns.doc(description='Get a SMS located in the inbox by his ID')
@api.doc(security='Bearer')
@jwt_required
@required_bearerAuth(environment_config["require_bearer"])
@required_basicAuth(environment_config["require_basic"])
@api.response(200, 'Success', inbox_item)
def get(self, id: int):
''' Get a SMS located in the inbox by his ID'''
Expand All @@ -80,12 +82,12 @@ def get(self, id: int):
@ns.doc(params={'id': 'ID of a SMS to delete'})
@ns.doc(description='Delete a SMS located in the inbox')
@ns.response(204, 'Success')
@api.doc(security='Bearer')
@jwt_required
@required_bearerAuth(environment_config["require_bearer"])
@required_basicAuth(environment_config["require_basic"])
def delete(self, id: int):
''' Delete a SMS located in the inbox'''
sms = inbox.query.filter_by(ID=id).first()
if(sms):
db_session.delete(sms)
db_session.commit()
return {'results': 'ok'}, 204
return {'results': 'ok'}, 204
16 changes: 9 additions & 7 deletions src/resources/v1/outbox.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from flask import request
from flask_restplus import Resource, reqparse
from flask_restx import Resource, reqparse
from flask_jwt_extended import ( jwt_required )
from database.models import outbox
from database.instance import db_session
from server.instance import server
from math import ceil
from models.outbox import outbox_item, outbox_items
from resources.decorators import ( required_bearerAuth, required_basicAuth )
from environment.instance import environment_config

api = server.api
ns = api.namespace('Outbox', description='Outbox operations', path='/')
Expand All @@ -22,8 +24,8 @@ class Outbox(Resource):
@api.doc(params={'after': {'description': 'Filter SMS send after a date', 'in': 'query', 'type': 'date'}})
@api.doc(params={'destination': {'description': 'Filter SMS send to a number', 'in': 'query', 'type': 'string'}})
@api.expect(pagination_arguments, validate=True)
@api.doc(security='Bearer')
@jwt_required
@required_bearerAuth(environment_config["require_bearer"])
@required_basicAuth(environment_config["require_basic"])
@api.response(200, 'Success', outbox_items)
def get(self):
''' Get all SMS located in the outbox'''
Expand Down Expand Up @@ -66,8 +68,8 @@ class OutboxID(Resource):

@ns.doc(params={'id': 'ID of a SMS to get'})
@ns.doc(description='Get a SMS located in the outbox by his ID')
@api.doc(security='Bearer')
@jwt_required
@required_bearerAuth(environment_config["require_bearer"])
@required_basicAuth(environment_config["require_basic"])
@api.response(200, 'Success', outbox_item)
def get(self, id: int):
''' Get a SMS located in the outbox by his ID'''
Expand All @@ -80,8 +82,8 @@ def get(self, id: int):
@ns.doc(params={'id': 'ID of a SMS to delete'})
@ns.doc(description='Delete a SMS located in the outbox')
@ns.response(204, 'Success')
@api.doc(security='Bearer')
@jwt_required
@required_bearerAuth(environment_config["require_bearer"])
@required_basicAuth(environment_config["require_basic"])
def delete(self, id: int):
''' Delete a SMS located in the outbox'''
sms = outbox.query.filter_by(ID=id).first()
Expand Down
8 changes: 5 additions & 3 deletions src/resources/v1/send.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from flask import request
from flask_restplus import Resource
from flask_restx import Resource
from flask_jwt_extended import ( jwt_required )
from server.instance import server
from environment.instance import gammu_smsd_config
from models.send import sms_post, send_result
from resources.decorators import ( required_bearerAuth, required_basicAuth )
from environment.instance import environment_config

import gammu
import gammu.smsd
Expand All @@ -18,8 +20,8 @@
class SendSMS(Resource):
@ns.expect(sms_post, validate=True)
@ns.doc(description='Send a SMS')
@api.doc(security='Bearer')
@jwt_required
@required_bearerAuth(environment_config["require_bearer"])
@required_basicAuth(environment_config["require_basic"])
@api.response(200, 'Success', send_result)
def post(self):
''' Send a SMS'''
Expand Down
Loading

0 comments on commit 21b4442

Please sign in to comment.