[英]sharing flask_httpauth 's HTTPBasicAuth across multiple Flask RestPlus endpoint files
I have a project that has the following structure我有一个具有以下结构的项目
app.py
|
endpoints(directory)
__init__.py
endpoints_1.py
endpoints_2.py
auth_endppints.py
auth_functionality_module.py
etc ...
app.py has the following content (simplified) app.py 有以下内容(简体)
# Import modules
..... imports
# Set document root
ROOT = os.path.dirname(os.path.abspath(__file__))
path = Path(ROOT)
parent = path.parent.absolute()
# Change settings based on config
config = ConfigParser()
config_auth = ConfigParser()
config.read(vv_settings.CONFIG_DIR)
config_auth.read(str(parent) + "/configuration/auth.ini")
# Get Secret Key
SECRET_KEY = config_auth['secret']['secret_key']
# Database config
SQLALCHEMY_DATABASE_URI = '<URI built in the code>'
"""
Create a parser object locally
"""
parser = request_parser.parser
# initiate
application = Flask(__name__)
api.init_app(application)
# configure application
# By default, show all endpoints (collapsed)
application.config.SWAGGER_UI_DOC_EXPANSION = 'list'
application.config.from_object(__name__)
application.config['SECRET_KEY'] = SECRET_KEY
application.config['PROPAGATE_EXCEPTIONS'] = False
application.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI
application.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
application.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(application)
class User(db.Model):
# Database model, currently very simple
__tablename__ = 'authUsers'
id = db.Column(db.Integer, primary_key=True)
domain = db.Column(db.String(50), index=True)
password_hash = db.Column(db.String(1000))
email = db.Column(db.String(50))
added = db.Column(db.Date)
expires = db.Column(db.Date)
termination = db.Column(db.Integer)
def hash_password(self, password):
"""
Code for password hashing
The hash_password method takes a plain password as argument and stores a hash of it with the user.
This method is called when a new user is registering with the server, or when the user changes the password.
:param password: string
:return: hashed password string
"""
self.password_hash = pwd_context.encrypt(password)
def verify_password(self, password):
"""
The verify_password() method takes a plain password as argument and returns True if the password is
correct or False if not.
This method is called whenever the user provides credentials and they need to be validated.
:param password:
:return: True / False
"""
verification = pwd_context.verify(password, self.password_hash)
return verification
# Method to generate token
def generate_auth_token(self):
s = Serializer(application.config['SECRET_KEY'])
return s.dumps({'id': self.id})
@staticmethod
def verify_auth_token(token):
s = Serializer(application.config['SECRET_KEY'])
try:
data = s.loads(token)
except SignatureExpired:
return None # valid token, but expired
except BadSignature:
return None # invalid token
user = User.query.get(data['id'])
return user
@application.before_request
def before_request():
"""
Add the User object and the db to Flask g.
:return: No return
"""
g.User = User()
g.db = db
"""
Representations
- Adds a response-type into the "Response content type" drop-down menu displayed in Swagger
- When selected, the APP will return the correct response-header and content type
- The default for flask-RESTPlus is application/json
"""
@api.representation('application/xml')
def application_xml(data, code, headers):
resp = representations.xml(data, code, headers)
resp.headers['Content-Type'] = 'application/xml'
return resp
@api.representation('application/json')
def application_json(data, code, headers):
resp = representations.application_json(data, code, headers)
resp.headers['Content-Type'] = 'application/json'
return resp
"""
Error handlers
- exceptions has now been imported from utils!
"""
def log_exception(exception_type):
# We want to know the arguments passed and the path so we can replicate the error
params = dict(request.args)
params['path'] = request.path
# Create the message and log
message = '%s occurred at %s with params=%s' % (exception_type, time.ctime(), params)
logger.exception(message, exc_info=True)
@application.errorhandler(exceptions.RemoteConnectionError)
def remote_connection_error_handler(e):
# Add the Exception to the log ensuring that exc_info is True so that a traceback is also logged
log_exception('RemoteConnectionError')
# Collect Arguments
args = parser.parse_args()
if args['content-type'] != 'application/xml':
return application_json({'message': str(e)},
504,
None)
else:
return application_xml({'message': str(e)},
504,
None)
if __name__ == '__main__':
application.debug = True
application.run(host="127.0.0.1", port=5000)
auth_endpoints.py contains the auth = HTTPBasicAuth(), also the @auth.verify_password decorated function and also imports the auth_functionality_module.py which contains the remaining apiAccess object found in this tutorial auth_endpoints.py 包含 auth = HTTPBasicAuth(),还有 @auth.verify_password 修饰函数,还导入了 auth_functionality_module.py,其中包含本教程中找到的剩余 apiAccess 对象
imports
...
from flask_httpauth import HTTPBasicAuth
"""
Auth settings
"""
# Reference
# https://blog.miguelgrinberg.com/post/restful-authentication-with-flask
acc = apiAccess.ApiAccess()
auth = HTTPBasicAuth()
# Password verification decorator
@auth.verify_password
def verify_password(username_or_token, password):
"""
Verifies the passwod or token.
This is the decorates auth method triggered by @auth.login_required
:param username_or_token: string
:param password: string
:return: True / False
"""
# first try to authenticate by token
user = g.User.verify_auth_token(username_or_token)
if not user:
# try to authenticate with username/password
user = g.User.query.filter_by(domain=username_or_token).first()
if not user or not user.verify_password(password):
return False
g.user = user
now = datetime.now().date()
if now > user.expires:
return False
else:
return True
"""
Namespace 1
"""
api1 = Namespace('auth', description='Endpoints for auth control, doc=False)
@api1.route("/token")
@api1.hide
class Token(Resource):
@auth.login_required
@api1.expect(parser, validate=True)
def get(self):
token = g.user.generate_auth_token()
return representations.application_json({'your token': token.decode('ascii')}, 200, None)
"""
Other resources follow as does a second Namespace
The other endpoint.py files in the endpoints directory contain namespaces and endpoints which I want to add @auth.verify_password
to. endpoints 目录中的其他 endpoint.py 文件包含我想要添加@auth.verify_password
的命名空间和端点。 Is there any way to initialise auth有什么方法可以初始化身份验证
auth = HTTPBasicAuth
and make it accessible and functional to the other namespaces并使其对其他命名空间具有可访问性和功能性
What I have tried我试过的
@application.before_request
which gives me RuntimeError: Working outside of application context
也在@application.before_request
中,这给了我RuntimeError: Working outside of application context
My ideal solution would be to have the auth = HTTPBasicAuth()
in app.py and to be able to import it into the endpoint files.我理想的解决方案是在 app.py 中有auth = HTTPBasicAuth()
并能够将其导入到端点文件中。 If anyone has done this and can provide code or a tutorial that would be great.如果有人这样做并且可以提供很棒的代码或教程。 I find it easier to solve these issues if I can see an example rather than just suggestions please.如果我能看到一个例子而不仅仅是建议,我发现解决这些问题更容易。 Thanks谢谢
The best way to organize this is (in my opinion) to move the auth
object to its own module, let's say authentication.py
:组织这个的最好方法是(在我看来)将auth
对象移动到它自己的模块,比如说authentication.py
:
from flask_httpauth import HTTPBasicAuth
auth = HTTPBasicAuth()
@auth.verify_password
def verify_password(username_or_token, password):
# ...
Then you can import the auth
object in any of your other modules, where you need to add authentication to an endpoint.然后,您可以在需要向端点添加身份验证的任何其他模块中导入auth
对象。 For example:例如:
from .authentication import auth
@app.route('/api/whatever')
@auth.login_required
def endpoint():
# ...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.