Skip to content

Commit

Permalink
295 implement token based auth (#296)
Browse files Browse the repository at this point in the history
* Support user login. (#294)

* Support user login. (#294)

* Support user login. (#294)

* 291 support token based auth (#293)

* 1. Added a new file "auth.py" to the "app/auth" directory, which contains a function "auth_required" that is used as a decorator for route functions that require authentication. This function checks for the presence of credentials in the request's authorization header and verifies them against the user model in the database. If the credentials are valid, the user object is added to the Flask's "g" object and the route function is called. If the credentials are missing or invalid, a 401 Unauthorized response is returned.

2. Updated the "notebook.py" file in the "app/routes" directory to import the "auth_required" function from the "auth.py" file. This function is then used as a decorator for the "get_all_notebooks" route function, which requires authentication. This ensures that only authenticated users can access the route.

3. Added the "@auth_required" decorator above the "get_all_notebooks" route function in the "notebook.py" file. This decorator ensures that the route function is only called if the user is authenticated. If the user is not authenticated, a 401 Unauthorized response is returned.

4. Added a new test case to the "test_notebook_route.py" file to test the authentication functionality of the "get_all_notebooks" route. This test case sends a GET request to the route without providing any credentials and asserts that the response status code is 401 Unauthorized.

5. Updated the commit message to include the changes made in the previous commits.

* 291 support token based auth (#293)

* 1. Added a new file "auth.py" to the "app/auth" directory, which contains a function "auth_required" that is used as a decorator for route functions that require authentication. This function checks for the presence of credentials in the request's authorization header and verifies them against the user model in the database. If the credentials are valid, the user object is added to the Flask's "g" object and the route function is called. If the credentials are missing or invalid, a 401 Unauthorized response is returned.

2. Updated the "notebook.py" file in the "app/routes" directory to import the "auth_required" function from the "auth.py" file. This function is then used as a decorator for the "get_all_notebooks" route function, which requires authentication. This ensures that only authenticated users can access the route.

3. Added the "@auth_required" decorator above the "get_all_notebooks" route function in the "notebook.py" file. This decorator ensures that the route function is only called if the user is authenticated. If the user is not authenticated, a 401 Unauthorized response is returned.

4. Added a new test case to the "test_notebook_route.py" file to test the authentication functionality of the "get_all_notebooks" route. This test case sends a GET request to the route without providing any credentials and asserts that the response status code is 401 Unauthorized.

5. Updated the commit message to include the changes made in the previous commits.

* 291 support token based auth (#293)

* 1. Added a new file "auth.py" to the "app/auth" directory, which contains a function "auth_required" that is used as a decorator for route functions that require authentication. This function checks for the presence of credentials in the request's authorization header and verifies them against the user model in the database. If the credentials are valid, the user object is added to the Flask's "g" object and the route function is called. If the credentials are missing or invalid, a 401 Unauthorized response is returned.

2. Updated the "notebook.py" file in the "app/routes" directory to import the "auth_required" function from the "auth.py" file. This function is then used as a decorator for the "get_all_notebooks" route function, which requires authentication. This ensures that only authenticated users can access the route.

3. Added the "@auth_required" decorator above the "get_all_notebooks" route function in the "notebook.py" file. This decorator ensures that the route function is only called if the user is authenticated. If the user is not authenticated, a 401 Unauthorized response is returned.

4. Added a new test case to the "test_notebook_route.py" file to test the authentication functionality of the "get_all_notebooks" route. This test case sends a GET request to the route without providing any credentials and asserts that the response status code is 401 Unauthorized.

5. Updated the commit message to include the changes made in the previous commits.

* 291 support token based auth (#293)

* 1. Added a new file "auth.py" to the "app/auth" directory, which contains a function "auth_required" that is used as a decorator for route functions that require authentication. This function checks for the presence of credentials in the request's authorization header and verifies them against the user model in the database. If the credentials are valid, the user object is added to the Flask's "g" object and the route function is called. If the credentials are missing or invalid, a 401 Unauthorized response is returned.

2. Updated the "notebook.py" file in the "app/routes" directory to import the "auth_required" function from the "auth.py" file. This function is then used as a decorator for the "get_all_notebooks" route function, which requires authentication. This ensures that only authenticated users can access the route.

3. Added the "@auth_required" decorator above the "get_all_notebooks" route function in the "notebook.py" file. This decorator ensures that the route function is only called if the user is authenticated. If the user is not authenticated, a 401 Unauthorized response is returned.

4. Added a new test case to the "test_notebook_route.py" file to test the authentication functionality of the "get_all_notebooks" route. This test case sends a GET request to the route without providing any credentials and asserts that the response status code is 401 Unauthorized.

5. Updated the commit message to include the changes made in the previous commits.

* Support user login. (#294)

* Support user login. (#294)

* Fix authentication error in test_notebook_route.py

* Refactor authentication logic and update API calls to use token-based authentication
  • Loading branch information
xuwenyihust authored Jul 30, 2024
1 parent 285b84f commit 2f00144
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 85 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,7 @@ webapp/yarn-error.log*
webapp/node_modules/*
node_modules/*
.DS_Store


server/.DS_Store
server/app_secrets.py
13 changes: 11 additions & 2 deletions server/app/auth/auth.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from flask import request, Response, g
from app.models.user import UserModel
from flask_jwt_extended import get_jwt_identity
from functools import wraps
import json

def auth_required(f):
def password_required(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
Expand All @@ -22,4 +23,12 @@ def decorated(*args, **kwargs):

g.user = user
return f(*args, **kwargs)
return decorated
return decorated

def identify_user(f):
@wraps(f)
def decorated(*args, **kwargs):
user_name = get_jwt_identity()
g.user = UserModel.query.filter_by(name=user_name).first()
return f(*args, **kwargs)
return decorated
16 changes: 8 additions & 8 deletions server/app/routes/login.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from flask import Blueprint, Response, g
from app.services.user import User
from app.auth.auth import auth_required
from flask_jwt_extended import create_access_token
from app.auth.auth import password_required
import logging
import json

Expand All @@ -9,16 +9,16 @@
logging.basicConfig(level=logging.INFO)

@login_blueprint.route('/login', methods=['POST'])
@auth_required
@password_required
def login():
logging.info(f"Logging in user: {g.user.name}")

# name = data.get('name', None)
# password = data.get('password', None)
# return User.validate_user_by_name(name=name, password=password)
access_token = create_access_token(identity=g.user.name)

return Response(
response=json.dumps({'message': 'Login successful'}),
response=json.dumps({
'message': 'Login successful',
'access_token': access_token
}),
status=200
)

25 changes: 17 additions & 8 deletions server/app/routes/notebook.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from flask import Blueprint, jsonify, request, g
from app.services.notebook import Notebook
from app.auth.auth import auth_required
from flask_jwt_extended import jwt_required
from app.auth.auth import identify_user
import logging

notebook_blueprint = Blueprint('notebook', __name__)
Expand All @@ -16,18 +17,22 @@ def notebook():
)

@notebook_blueprint.route('/notebook/all', methods=['GET'])
@auth_required
@jwt_required()
@identify_user
def get_all_notebooks():
logging.info(f"Getting all notebooks by user: {g.user.name}")
return Notebook.get_all_notebooks()

@notebook_blueprint.route('/notebook/<path:notebook_path>', methods=['GET'])
@auth_required
@jwt_required()
@identify_user
def get_notebook_by_path(notebook_path):
logging.info(f"Getting notebook with path: {notebook_path} by user: {g.user.name}")
return Notebook.get_notebook_by_path(notebook_path=notebook_path)

@notebook_blueprint.route('/notebook', methods=['POST'])
@auth_required
@jwt_required()
@identify_user
def create_notebook():
data = request.get_json()
notebook_name = data.get('name', None)
Expand All @@ -36,21 +41,24 @@ def create_notebook():
return Notebook.create_notebook_with_init_cells(notebook_name=notebook_name, notebook_path=notebook_path)

@notebook_blueprint.route('/notebook/<path:notebook_path>', methods=['PUT'])
@auth_required
@jwt_required()
@identify_user
def update_notebook(notebook_path):
data = request.get_json()
content = data.get('content', None)
logging.info(f"Updating notebook with path: {notebook_path} by user: {g.user.name}")
return Notebook.update_notebook(notebook_path=notebook_path, content=content)

@notebook_blueprint.route('/notebook/<path:notebook_path>', methods=['DELETE'])
@auth_required
@jwt_required()
@identify_user
def delete_notebook(notebook_path):
logging.info(f"Deleting notebook with path: {notebook_path}")
return Notebook.delete_notebook_by_path(notebook_path=notebook_path)

@notebook_blueprint.route('/notebook/<path:notebook_path>', methods=['PATCH'])
@auth_required
@jwt_required()
@identify_user
def rename_or_move_notebook(notebook_path):
data = request.get_json()
if 'newName' in data:
Expand All @@ -63,7 +71,8 @@ def rename_or_move_notebook(notebook_path):
return Notebook.move_notebook(notebook_path=notebook_path, new_notebook_path=new_notebook_path)

@notebook_blueprint.route('/notebook/spark_app/<path:notebook_path>', methods=['GET'])
@auth_required
@jwt_required()
@identify_user
def get_spark_app_by_notebook_path(notebook_path):
logging.info(f"Get spark apps by notebook path: {notebook_path}")
return Notebook.get_spark_app_by_notebook_path(notebook_path)
Expand Down
3 changes: 2 additions & 1 deletion server/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ Flask==3.0.3
Flask-Cors==4.0.1
requests==2.32.2
Flask-SQLAlchemy==3.1.1
psycopg2-binary==2.9.9
psycopg2-binary==2.9.9
flask-jwt-extended==4.6.0
11 changes: 11 additions & 0 deletions server/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from app.routes.kernel import kernel_blueprint
from app.routes.spark_app import spark_app_blueprint
from app.routes.login import login_blueprint
from flask_jwt_extended import JWTManager
from config import DevelopmentConfig, IntegrationTestingConfig, TestingConfig

def create_app():
Expand All @@ -19,6 +20,15 @@ def create_app():
elif os.environ.get('ENV', 'development') == 'integration':
app.config.from_object(IntegrationTestingConfig)

# Set the secret key for JWT
try:
from app_secrets import JWT_SECRET_KEY
except ImportError:
JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY', 'default_secret_key')

app.config['JWT_SECRET_KEY'] = JWT_SECRET_KEY
jwt = JWTManager(app)

db.init_app(app)

allowed_origins = ["http://localhost:5001", "http://localhost:3000"]
Expand All @@ -38,5 +48,6 @@ def create_app():
app.register_blueprint(login_blueprint)



if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5002)
Loading

0 comments on commit 2f00144

Please sign in to comment.