-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* wip: add user login/activity * add login/auth to test client * make password pass validation * test user creation * add connexion specific function for decoding jwts * setup security with flask-jwt-extended * add openapi updates to security * add flask-jwt-extended to requirements * hacky way to change suffix login and register endpoints * split resources to auth and data * split schemas to auth and data * modify inits to include all functions * update example config * fix style and add assertions * keep flask under 2.0 * keep flask migrate under 3.0.0
- Loading branch information
Showing
16 changed files
with
243 additions
and
37 deletions.
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
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
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
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
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
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
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,34 @@ | ||
from .data import ( | ||
DatasetsView, | ||
StudiesView, | ||
AnalysesView, | ||
ConditionsView, | ||
ImagesView, | ||
PointsView, | ||
PointValueView, | ||
DatasetsListView, | ||
StudiesListView, | ||
AnalysesListView, | ||
ImagesListView, | ||
) | ||
|
||
from .auth import ( | ||
RegisterView, | ||
LoginView | ||
) | ||
|
||
__all__ = [ | ||
"DatasetsView", | ||
"StudiesView", | ||
"AnalysesView", | ||
"ConditionsView", | ||
"ImagesView", | ||
"PointsView", | ||
"PointValueView", | ||
"StudiesListView", | ||
"AnalysesListView", | ||
"ImagesListView", | ||
"DatasetsListView", | ||
"RegisterView", | ||
"LoginView" | ||
] |
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,85 @@ | ||
# import datetime | ||
|
||
from flask import jsonify, request | ||
from flask.views import MethodView | ||
from flask_security.utils import verify_password | ||
from flask_jwt_extended import get_jwt_identity, jwt_required, create_access_token | ||
from webargs.flaskparser import parser | ||
|
||
from ..models.auth import User # , user_datastore | ||
from ..schemas import UserSchema # noqa E401 | ||
from ..database import db | ||
from ..core import jwt | ||
|
||
|
||
# Register a callback function that takes whatever object is passed in as the | ||
# identity when creating JWTs and converts it to a JSON serializable format. | ||
@jwt.user_identity_loader | ||
def user_identity_lookup(user): | ||
return user.id | ||
|
||
|
||
# Register a callback function that loades a user from your database whenever | ||
# a protected route is accessed. This should return any python object on a | ||
# successful lookup, or None if the lookup failed for any reason (for example | ||
# if the user has been deleted from the database). | ||
@jwt.user_lookup_loader | ||
def user_lookup_callback(_jwt_header, jwt_data): | ||
identity = jwt_data["sub"] | ||
return User.query.filter_by(id=identity).one_or_none() | ||
|
||
|
||
class RegisterView(MethodView): | ||
_model = User | ||
|
||
@property | ||
def schema(self): | ||
return globals()[self._model.__name__ + 'Schema'] | ||
|
||
@jwt_required | ||
def get(self): | ||
return get_jwt_identity() | ||
|
||
def post(self, **kwargs): | ||
data = parser.parse(self.schema, request) | ||
record = self._model() | ||
# Store all models so we can atomically update in one commit | ||
to_commit = [] | ||
|
||
# Update all non-nested attributes | ||
for k, v in data.items(): | ||
setattr(record, k, v) | ||
|
||
to_commit.append(record) | ||
|
||
db.session.add_all(to_commit) | ||
db.session.commit() | ||
|
||
return self.schema().dump(record) | ||
|
||
|
||
class LoginView(MethodView): | ||
_model = User | ||
|
||
@property | ||
def schema(self): | ||
return globals()[self._model.__name__ + 'Schema'] | ||
|
||
def post(self, **kwargs): | ||
login_schema = self.schema(only=('email', 'password')) | ||
data = login_schema.load(request.json) | ||
# do not want the encrypted password | ||
data['password'] = request.json.get('password') | ||
user = self._model.query.filter_by(email=data['email']).one_or_none() | ||
if not user or not verify_password(data['password'], user.password): | ||
abort(403, 'incorrect email or password') | ||
|
||
user.access_token = create_access_token(identity=user) | ||
|
||
return self.schema(only=('access_token',)).dump(user) | ||
|
||
|
||
def abort(code, message=''): | ||
""" JSONified abort """ | ||
from flask import abort, make_response | ||
abort(make_response(jsonify(message=message), code)) |
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
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
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,33 @@ | ||
from marshmallow import ( | ||
fields, | ||
post_load, | ||
validates, | ||
ValidationError, | ||
EXCLUDE | ||
) | ||
from flask_security.utils import encrypt_password | ||
|
||
from .data import BaseSchema | ||
|
||
|
||
class UserSchema(BaseSchema): | ||
email = fields.Email(required=True) | ||
name = fields.Str(required=True, description='User full name') | ||
username = fields.Str(description='User name', dump_only=True) | ||
password = fields.Str(load_only=True, | ||
description='Password. Minimum 6 characters.') | ||
access_token = fields.Str(dump_only=True) | ||
|
||
@validates('password') | ||
def validate_pass(self, value): | ||
if len(value) < 6: | ||
raise ValidationError('Password must be at least 6 characters.') | ||
|
||
@post_load() | ||
def encrypt_password(self, in_data, **kwargs): | ||
if 'password' in in_data: | ||
in_data['password'] = encrypt_password(in_data['password']) | ||
return in_data | ||
|
||
class Meta: | ||
unknown = EXCLUDE |
File renamed without changes.
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 @@ | ||
def test_create_user(auth_client): | ||
new_user = { | ||
'email': '[email protected]', | ||
'name': "fake name", | ||
'username': 'user', | ||
'password': 'more than six characters' | ||
} | ||
auth_client.post("/api/register", data=new_user) | ||
login_resp = auth_client.post( | ||
"/api/login", | ||
data={ | ||
'email': new_user['email'], | ||
'password': new_user['password'], | ||
} | ||
) | ||
|
||
assert login_resp.status_code == 200 | ||
assert 'access_token' in login_resp.json.keys() |
Oops, something went wrong.