-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
546 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[run] | ||
include= | ||
*project/* | ||
omit = | ||
venv/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -99,3 +99,9 @@ ENV/ | |
|
||
# mypy | ||
.mypy_cache/ | ||
|
||
.idea | ||
|
||
pylintReport.txt | ||
db.sqlite3 | ||
_build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
language: python | ||
sudo: false | ||
cache: false | ||
python: | ||
- '3.6' | ||
install: | ||
- pip install -r requirements-tests.txt | ||
|
||
script: | ||
- coverage erase | ||
- coverage run -m unittest | ||
after_success: | ||
- coverage combine | ||
- coveralls | ||
|
||
notifications: | ||
email: | ||
- a.vara.1986@gmail.com |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
FROM python:3.6.4-alpine3.7 | ||
|
||
RUN apk add --update curl gcc g++ libffi-dev openssl-dev python3-dev \ | ||
&& rm -rf /var/cache/apk/* | ||
RUN ln -s /usr/include/locale.h /usr/include/xlocale.h | ||
|
||
ENV PYTHONUNBUFFERED=1 ENVIRONMENT=pre APP_HOME=/microservice/ | ||
|
||
RUN mkdir $APP_HOME | ||
WORKDIR $APP_HOME | ||
ADD requirement*.txt $APP_HOME | ||
RUN pip install -r requirements-docker.txt | ||
ADD . $APP_HOME | ||
|
||
EXPOSE 5000 | ||
|
||
CMD ["gunicorn", "--worker-class", "eventlet", "--workers", "8", "--log-level", "INFO", "--bind", "0.0.0.0:5000", "manage:app"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,6 @@ | ||
# oauth | ||
# oauth | ||
OAuth with JWT Python Microservices | ||
|
||
[![Build Status](https://travis-ci.org/python-microservices/oauth.svg?branch=master)](https://travis-ci.org/python-microservices/oauth) | ||
[![Coverage Status](https://coveralls.io/repos/github/python-microservices/oauth/badge.svg?branch=master)](https://coveralls.io/github/python-microservices/oauth?branch=master) | ||
[![Requirements Status](https://requires.io/github/python-microservices/oauth/requirements.svg?branch=master)](https://requires.io/github/python-microservices/oauth/requirements/?branch=master) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# encoding: utf-8 | ||
from flask_script import Manager | ||
|
||
from project import create_app | ||
|
||
app, db = create_app() | ||
|
||
manager = Manager(app) | ||
|
||
|
||
@manager.command | ||
def create_db(): | ||
"""Creates the db tables.""" | ||
db.create_all() | ||
|
||
|
||
@manager.command | ||
def drop_db(): | ||
"""Drops the db tables.""" | ||
db.drop_all() | ||
|
||
|
||
if __name__ == '__main__': | ||
manager.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# encoding: utf-8 | ||
|
||
import os | ||
|
||
from flasgger import Swagger | ||
from flask import Flask | ||
|
||
from project.config import CONFIG | ||
|
||
__author__ = "Alberto Vara" | ||
__email__ = "a.vara.1986@gmail.com" | ||
__version__ = "0.0.1" | ||
|
||
ENVIRONMENT = os.environ.get("ENVIRONMENT", "default") | ||
|
||
SWAGGER_CONFIG = { | ||
"headers": [ | ||
], | ||
"specs": [ | ||
{ | ||
"endpoint": 'apispec_1', | ||
"route": '{application_root}/apispec_1.json', | ||
"rule_filter": lambda rule: True, # all in | ||
"model_filter": lambda tag: True, # all in | ||
} | ||
], | ||
"info": { | ||
"title": "API ", | ||
"description": "API para...", | ||
"contact": { | ||
"responsibleOrganization": "ME", | ||
"responsibleDeveloper": "Me", | ||
"email": "me@me.com", | ||
}, | ||
"version": "0.0.1" | ||
}, | ||
"securityDefinitions": { | ||
"APIKeyHeader": {"type": "apiKey", "name": "Authorization", "in": "header"}, | ||
}, | ||
"static_url_path": "{application_root}/flasgger_static", | ||
"swagger_ui": True, | ||
"uiversion": 2, | ||
"specs_route": "/apidocs/", | ||
"basePath": "{application_root}" | ||
} | ||
|
||
|
||
class PrefixMiddleware(object): | ||
|
||
def __init__(self, app, prefix=''): | ||
self.app = app | ||
self.prefix = prefix | ||
|
||
def __call__(self, environ, start_response): | ||
|
||
if environ['PATH_INFO'].startswith(self.prefix): | ||
environ['PATH_INFO'] = environ['PATH_INFO'][len(self.prefix):] | ||
environ['SCRIPT_NAME'] = self.prefix | ||
return self.app(environ, start_response) | ||
else: | ||
start_response('404', [('Content-Type', 'text/plain')]) | ||
return ["This url does not belong to the app.".encode()] | ||
|
||
|
||
def create_app(): | ||
from project.models import db | ||
from project.views import views_bp as views_blueprint | ||
from project.views.oauth import jwt, bcrypt | ||
environment = os.environ.get("ENVIRONMENT", "default") | ||
|
||
app = Flask(__name__) | ||
app.config.from_object(CONFIG[environment]) | ||
app.wsgi_app = PrefixMiddleware(app.wsgi_app, prefix=app.config["APPLICATION_ROOT"]) | ||
|
||
db.init_app(app) | ||
bcrypt.init_app(app) | ||
jwt.init_app(app) | ||
|
||
SWAGGER_CONFIG["specs"][0]["route"] = SWAGGER_CONFIG["specs"][0]["route"].format( | ||
application_root=app.config["APPLICATION_ROOT"] | ||
) | ||
SWAGGER_CONFIG["static_url_path"] = SWAGGER_CONFIG["static_url_path"].format( | ||
application_root=app.config["APPLICATION_ROOT"] | ||
) | ||
SWAGGER_CONFIG["specs_route"] = SWAGGER_CONFIG["specs_route"].format( | ||
application_root=app.config["APPLICATION_ROOT"] | ||
) | ||
SWAGGER_CONFIG["basePath"] = SWAGGER_CONFIG["basePath"].format( | ||
application_root=app.config["APPLICATION_ROOT"] | ||
) | ||
Swagger(app, config=SWAGGER_CONFIG) | ||
|
||
app.register_blueprint(views_blueprint) | ||
with app.test_request_context(): | ||
db.create_all() | ||
return app, db |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# coding=utf-8 | ||
import os | ||
|
||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | ||
|
||
|
||
class Config: | ||
"""Default configuration for all environments""" | ||
|
||
DEBUG = False | ||
TESTING = False | ||
APP_NAME = "Oauth" | ||
APPLICATION_ROOT = "/oauth" | ||
SQLALCHEMY_TRACK_MODIFICATIONS = True | ||
SECRET_KEY = os.environ.get("SECRET_KEY") or "gjr39dkjn344_!67#" | ||
|
||
|
||
class TestConfig(Config): | ||
"""Configuration to run tests""" | ||
|
||
DEBUG = True | ||
TESTING = True | ||
DATABASE = os.path.join(BASE_DIR, "db_test.sqlite3") | ||
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(BASE_DIR, "db_test.sqlite3") | ||
|
||
|
||
class DevConfig(Config): | ||
"""Configuration to run in local environments""" | ||
|
||
DEBUG = True | ||
TESTING = True | ||
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(BASE_DIR, "db.sqlite3") | ||
|
||
|
||
class PreConfig(Config): | ||
"""Configuration to run with docker and kubernetes in Preproduction""" | ||
SQLALCHEMY_TRACK_MODIFICATIONS = False | ||
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(BASE_DIR, "db.sqlite3") | ||
|
||
|
||
class ProdConfig(Config): | ||
"""Configuration to run with docker and kubernetes in Production""" | ||
SQLALCHEMY_TRACK_MODIFICATIONS = False | ||
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(BASE_DIR, "prod.db") | ||
|
||
|
||
CONFIG = { | ||
"test": TestConfig, | ||
"dev": DevConfig, | ||
"pre": PreConfig, | ||
"prod": ProdConfig, | ||
"default": DevConfig | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# encoding: utf-8 | ||
from __future__ import absolute_import, print_function, unicode_literals | ||
|
||
from flask_sqlalchemy import SQLAlchemy | ||
|
||
db = SQLAlchemy() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# encoding: utf-8 | ||
from __future__ import absolute_import, print_function, unicode_literals | ||
|
||
import datetime | ||
|
||
from flask_login import UserMixin | ||
from sqlalchemy import Column, Integer, String, Boolean | ||
|
||
from project.models import db | ||
|
||
|
||
class User(db.Model, UserMixin): | ||
__tablename__ = 'auth_user' | ||
date_joined = db.Column(db.DateTime, default=datetime.datetime.utcnow()) | ||
|
||
id = Column(Integer, primary_key=True, autoincrement=True) | ||
email = Column(String, default="") | ||
first_name = Column(String, default="") | ||
last_name = Column(String, default="") | ||
username = Column(String, nullable=False) | ||
password = Column(String, nullable=False) | ||
is_superuser = Column(Boolean, default=False) | ||
is_staff = Column(Boolean, default=False) | ||
is_active = Column(Boolean, default=True) | ||
|
||
def __init__(self, username, password): | ||
from project.views.oauth import bcrypt | ||
self.username = username | ||
self.password = bcrypt.generate_password_hash( | ||
password, 13 | ||
).decode() |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import json | ||
import os | ||
import unittest | ||
from typing import Dict, List, Union, Text | ||
|
||
from project import create_app | ||
from project.models.models import User, db | ||
|
||
def _format_response(response: Text = "") -> Union[List, Dict]: | ||
return json.loads(response) | ||
|
||
|
||
class FlaskrTestCase(unittest.TestCase): | ||
|
||
def setUp(self): | ||
os.environ["ENVIRONMENT"] = "test" | ||
self.app, self.db = create_app() | ||
self.base_url = self.app.config["APPLICATION_ROOT"] | ||
self.client = self.app.test_client() | ||
|
||
def tearDown(self): | ||
os.unlink(self.app.config['DATABASE']) | ||
|
||
def _create_user(self, username, password): | ||
with self.app.test_request_context(): | ||
user = User( | ||
username=username, | ||
password=password | ||
) | ||
# insert the user | ||
db.session.add(user) | ||
db.session.commit() | ||
return user.id | ||
|
||
def test_home(self): | ||
response = self.client.get('/') | ||
self.assertEqual(response.status_code, 404) | ||
|
||
def test_protected_view_error(self): | ||
response = self.client.get('{base_url}/check-token'.format(base_url=self.base_url)) | ||
self.assertEqual(response.status_code, 401) | ||
self.assertEqual(_format_response(response.data)["description"], "Request does not contain an access token") | ||
self.assertEqual(_format_response(response.data)["error"], "Authorization Required") | ||
|
||
def test_login_error(self): | ||
response = self.client.post('{base_url}/login'.format(base_url=self.base_url), | ||
data={"username": "", "password": ""}) | ||
self.assertEqual(response.status_code, 401) | ||
self.assertEqual(_format_response(response.data)["message"], 'Bad username and/or password') | ||
|
||
def test_login_ok(self): | ||
username = "test" | ||
password = "1234" | ||
self._create_user(username, password) | ||
response = self.client.post('{base_url}/login'.format(base_url=self.base_url), | ||
data={"username": username, "password": password}) | ||
self.assertEqual(response.status_code, 200) | ||
result = _format_response(response.data)["access_token"] | ||
self.assertGreater(len(result), 0) | ||
|
||
# check protected | ||
response = self.client.get('{base_url}/check-token'.format(base_url=self.base_url), | ||
headers={'authorization': 'JWT {}'.format(result)}) | ||
self.assertEqual(response.status_code, 200) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# coding=utf-8 | ||
from __future__ import unicode_literals, print_function, absolute_import, division | ||
|
||
from flask import Blueprint | ||
|
||
views_bp = Blueprint('views', __name__, static_url_path='/static') | ||
|
||
from project.views import views |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from flask_bcrypt import Bcrypt | ||
from flask_jwt import JWT | ||
|
||
from project.models.models import User | ||
|
||
bcrypt = Bcrypt() | ||
|
||
|
||
def authenticate(username, password): | ||
user = User.query.filter_by(username=username).first() | ||
if user and bcrypt.check_password_hash( | ||
user.password, password | ||
): | ||
return user | ||
|
||
|
||
def identity(payload): | ||
user_id = payload['identity'] | ||
return User.query.filter_by(id=user_id).first() | ||
|
||
|
||
jwt = JWT(authentication_handler=authenticate, identity_handler=identity) |
Oops, something went wrong.