2022-05-27 13:28:26 +02:00
|
|
|
from typing import Dict, Tuple, Union
|
|
|
|
|
2022-05-27 13:34:32 +02:00
|
|
|
from flask import Blueprint, Response, request
|
2022-05-27 13:28:26 +02:00
|
|
|
|
|
|
|
from fittrackee import db
|
2022-05-28 15:36:18 +02:00
|
|
|
from fittrackee.oauth2.models import OAuth2Client
|
2022-05-27 15:51:40 +02:00
|
|
|
from fittrackee.oauth2.server import require_auth
|
2022-05-28 15:36:18 +02:00
|
|
|
from fittrackee.responses import (
|
|
|
|
HttpResponse,
|
|
|
|
InvalidPayloadErrorResponse,
|
|
|
|
NotFoundErrorResponse,
|
|
|
|
)
|
2022-05-27 13:28:26 +02:00
|
|
|
from fittrackee.users.models import User
|
|
|
|
|
|
|
|
from .client import create_oauth_client
|
2022-05-27 13:34:32 +02:00
|
|
|
from .server import authorization_server
|
2022-05-27 13:28:26 +02:00
|
|
|
|
|
|
|
oauth_blueprint = Blueprint('oauth', __name__)
|
|
|
|
|
|
|
|
EXPECTED_METADATA_KEYS = [
|
|
|
|
'client_name',
|
|
|
|
'client_uri',
|
|
|
|
'redirect_uris',
|
|
|
|
'scope',
|
|
|
|
]
|
2022-05-28 15:36:18 +02:00
|
|
|
DEFAULT_PER_PAGE = 5
|
|
|
|
|
|
|
|
|
|
|
|
@oauth_blueprint.route('/oauth/apps', methods=['GET'])
|
|
|
|
@require_auth()
|
|
|
|
def get_clients(auth_user: User) -> Dict:
|
|
|
|
params = request.args.copy()
|
|
|
|
page = int(params.get('page', 1))
|
|
|
|
per_page = DEFAULT_PER_PAGE
|
|
|
|
clients_pagination = (
|
|
|
|
OAuth2Client.query.filter_by(user_id=auth_user.id)
|
|
|
|
.order_by(OAuth2Client.id.desc())
|
|
|
|
.paginate(page, per_page, False)
|
|
|
|
)
|
|
|
|
clients = clients_pagination.items
|
|
|
|
return {
|
|
|
|
'status': 'success',
|
|
|
|
'data': {
|
|
|
|
'clients': [
|
|
|
|
client.serialize(with_secret=False) for client in clients
|
|
|
|
]
|
|
|
|
},
|
|
|
|
'pagination': {
|
|
|
|
'has_next': clients_pagination.has_next,
|
|
|
|
'has_prev': clients_pagination.has_prev,
|
|
|
|
'page': clients_pagination.page,
|
|
|
|
'pages': clients_pagination.pages,
|
|
|
|
'total': clients_pagination.total,
|
|
|
|
},
|
|
|
|
}
|
2022-05-27 13:28:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@oauth_blueprint.route('/oauth/apps', methods=['POST'])
|
2022-05-27 15:51:40 +02:00
|
|
|
@require_auth()
|
2022-05-27 13:28:26 +02:00
|
|
|
def create_client(auth_user: User) -> Union[HttpResponse, Tuple[Dict, int]]:
|
|
|
|
client_metadata = request.get_json()
|
|
|
|
if not client_metadata:
|
|
|
|
return InvalidPayloadErrorResponse(
|
|
|
|
message='OAuth client metadata missing'
|
|
|
|
)
|
|
|
|
|
|
|
|
missing_keys = [
|
|
|
|
key
|
|
|
|
for key in EXPECTED_METADATA_KEYS
|
|
|
|
if key not in client_metadata.keys()
|
|
|
|
]
|
|
|
|
if missing_keys:
|
|
|
|
return InvalidPayloadErrorResponse(
|
|
|
|
message=(
|
|
|
|
'OAuth client metadata missing keys: '
|
|
|
|
f'{", ".join(missing_keys)}'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
new_client = create_oauth_client(client_metadata, auth_user)
|
|
|
|
db.session.add(new_client)
|
|
|
|
db.session.commit()
|
|
|
|
return (
|
|
|
|
{
|
|
|
|
'status': 'created',
|
2022-05-28 15:36:18 +02:00
|
|
|
'data': {'client': new_client.serialize(with_secret=True)},
|
2022-05-27 13:28:26 +02:00
|
|
|
},
|
|
|
|
201,
|
|
|
|
)
|
2022-05-27 13:34:32 +02:00
|
|
|
|
|
|
|
|
2022-05-28 15:36:18 +02:00
|
|
|
@oauth_blueprint.route('/oauth/apps/<string:client_id>', methods=['GET'])
|
|
|
|
@require_auth()
|
|
|
|
def get_client(auth_user: User, client_id: str) -> Union[Dict, HttpResponse]:
|
|
|
|
client = OAuth2Client.query.filter_by(
|
|
|
|
id=client_id,
|
|
|
|
user_id=auth_user.id,
|
|
|
|
).first()
|
|
|
|
|
|
|
|
if not client:
|
|
|
|
return NotFoundErrorResponse('OAuth client not found')
|
|
|
|
|
|
|
|
return {
|
|
|
|
'status': 'success',
|
|
|
|
'data': {'client': client.serialize(with_secret=False)},
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@oauth_blueprint.route('/oauth/apps/<string:client_id>', methods=['DELETE'])
|
|
|
|
@require_auth()
|
|
|
|
def delete_client(
|
|
|
|
auth_user: User, client_id: str
|
|
|
|
) -> Union[Tuple[Dict, int], HttpResponse]:
|
|
|
|
client = OAuth2Client.query.filter_by(
|
|
|
|
id=client_id,
|
|
|
|
user_id=auth_user.id,
|
|
|
|
).first()
|
|
|
|
|
|
|
|
if not client:
|
|
|
|
return NotFoundErrorResponse('OAuth client not found')
|
|
|
|
|
|
|
|
db.session.delete(client)
|
|
|
|
db.session.commit()
|
|
|
|
return {'status': 'no content'}, 204
|
|
|
|
|
|
|
|
|
2022-05-27 13:34:32 +02:00
|
|
|
@oauth_blueprint.route('/oauth/authorize', methods=['POST'])
|
2022-05-27 15:51:40 +02:00
|
|
|
@require_auth()
|
2022-05-27 13:34:32 +02:00
|
|
|
def authorize(auth_user: User) -> Response:
|
|
|
|
data = request.form
|
|
|
|
if not data or 'client_id' not in data or 'response_type' not in data:
|
|
|
|
return InvalidPayloadErrorResponse()
|
|
|
|
|
|
|
|
authorization_server.get_consent_grant(end_user=auth_user)
|
|
|
|
return authorization_server.create_authorization_response(
|
|
|
|
grant_user=auth_user
|
|
|
|
)
|
2022-05-27 14:08:07 +02:00
|
|
|
|
|
|
|
|
|
|
|
@oauth_blueprint.route('/oauth/token', methods=['POST'])
|
|
|
|
def issue_token() -> Response:
|
|
|
|
return authorization_server.create_token_response()
|
2022-05-27 14:46:03 +02:00
|
|
|
|
|
|
|
|
|
|
|
@oauth_blueprint.route('/oauth/revoke', methods=['POST'])
|
|
|
|
def revoke_token() -> Response:
|
|
|
|
return authorization_server.create_endpoint_response('revocation')
|