Skip to content

Commit

Permalink
Create new async OAuthClient resource, blueprint & helper functions […
Browse files Browse the repository at this point in the history
…NHUB-528]
  • Loading branch information
devketanpro committed Jul 29, 2024
1 parent b68c267 commit aa65c21
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 78 deletions.
9 changes: 4 additions & 5 deletions newsroom/oauth_clients/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import superdesk
from flask import Blueprint
from flask_babel import lazy_gettext
from .clients import ClientResource, ClientService

blueprint = Blueprint("oauth_clients", __name__)
from superdesk.core.module import Module

from . import views # noqa
from .clients_async import clients_model_config, clients_endpoints

module = Module(name="newsroom.oauth_clients", resources=[clients_model_config], endpoints=[clients_endpoints])


def init_app(app):
superdesk.register_resource("oauth_clients", ClientResource, ClientService, _app=app)
app.settings_app(
"oauth_clients",
lazy_gettext("OAuth Clients"),
Expand Down
29 changes: 0 additions & 29 deletions newsroom/oauth_clients/clients.py

This file was deleted.

47 changes: 47 additions & 0 deletions newsroom/oauth_clients/clients_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from content_api import MONGO_PREFIX
from typing import Optional, Annotated, List, Dict, Union
from superdesk.core.resources import ResourceModel, ResourceConfig, MongoResourceConfig
from superdesk.core.resources.service import AsyncResourceService
from superdesk.core.web import EndpointGroup
from pydantic import Field
import logging
from bson import ObjectId


class ClientResource(ResourceModel):
id: Annotated[Union[str, ObjectId], Field(alias="_id")] = None
name: str
password: str
last_active: Optional[str] = None
etag: Annotated[Optional[str], Field(alias="_etag")] = None


class ClientService(AsyncResourceService[ClientResource]):
"""Service class for managing OAuthClient resources"""

resource_name = "oauth_clients"

async def get_all_client(self) -> List[Dict]:
try:
# Collect all items asynchronously
clients = [client async for client in self.get_all()]

# Convert clients to list of dictionaries
return [item.dict(by_alias=True, exclude_unset=True) for item in clients]

except Exception as e:
logging.error(f"Error retrieving data from clients: {e}")
return []


clients_model_config = ResourceConfig(
name="oauth_clients",
data_class=ClientResource,
mongo=MongoResourceConfig(
prefix=MONGO_PREFIX,
),
elastic=None,
service=ClientService,
)

clients_endpoints = EndpointGroup("oauth_clients", __name__)
105 changes: 63 additions & 42 deletions newsroom/oauth_clients/views.py
Original file line number Diff line number Diff line change
@@ -1,82 +1,103 @@
import re

import flask
from bson import ObjectId
import bcrypt
from flask import jsonify, current_app as app
from typing import Optional

from flask_babel import gettext
from superdesk import get_resource_service
from pydantic import BaseModel
from werkzeug.exceptions import NotFound

from newsroom.decorator import admin_only, account_manager_only
from newsroom.oauth_clients import blueprint
from newsroom.utils import query_resource, find_one, get_json_or_400
from superdesk.utils import gen_password
from superdesk.core.web import Request, Response
from superdesk.core.resources.fields import ObjectId

from newsroom.utils import get_json_or_400_async
from newsroom.decorator import admin_only, account_manager_only
from .clients_async import clients_endpoints, ClientService, ClientResource

def get_settings_data():

async def get_settings_data():
data = await ClientService().get_all_client()
return {
"oauth_clients": list(query_resource("oauth_clients")),
"oauth_clients": data,
}


@blueprint.route("/oauth_clients/search", methods=["GET"])
class ClientSearchArgs(BaseModel):
q: Optional[str] = None


class ClientArgs(BaseModel):
client_id: ObjectId


@clients_endpoints.endpoint("/oauth_clients/search", methods=["GET"])
@account_manager_only
def search():
async def search(args: None, params: ClientSearchArgs, request: Request) -> Response:
lookup = None
if flask.request.args.get("q"):
regex = re.compile(".*{}.*".format(flask.request.args.get("q")), re.IGNORECASE)
if params.q:
regex = re.compile(f".*{re.escape(params.q)}.*", re.IGNORECASE)
lookup = {"name": regex}
companies = list(query_resource("oauth_clients", lookup=lookup))
return jsonify(companies), 200
cursor = await ClientService().search(lookup)
data = await cursor.to_list_raw()
return Response(data, 200, ())


@blueprint.route("/oauth_clients/new", methods=["POST"])
@clients_endpoints.endpoint("/oauth_clients/new", methods=["POST"])
@account_manager_only
def create():
async def create(request: Request) -> Response:
"""
Creates the client with given client id
"""
client = get_json_or_400()
client = await get_json_or_400_async(request)
if not isinstance(client, dict):
return request.abort(400)

password = gen_password()
new_company = {
doc = {
"name": client.get("name"),
"password": bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode(),
}

ids = get_resource_service("oauth_clients").post([new_company])
return jsonify({"success": True, "_id": ids[0], "password": password}), 201
new_client = ClientResource.model_validate(doc)
ids = await ClientService().create([new_client])
return Response({"success": True, "_id": ids[0], "password": password}, 201, ())


@blueprint.route("/oauth_clients/<_id>", methods=["GET", "POST"])
@clients_endpoints.endpoint("/oauth_clients/<string:client_id>", methods=["GET", "POST"])
@account_manager_only
def edit(_id):
async def edit(args: ClientArgs, params: None, request: Request) -> Response:
"""
Edits the client with given client id
"""
client = find_one("oauth_clients", _id=ObjectId(_id))

if not client:
service = ClientService()
original = await service.find_by_id(args.client_id)
if not original:
return NotFound(gettext("Client not found"))
elif request.method == "GET":
return Response(original, 200, ())

request_json = await get_json_or_400_async(request)
if not isinstance(request_json, dict):
return request.abort(400)

if flask.request.method == "POST":
client = get_json_or_400()
updates = {}
updates["name"] = client.get("name")
get_resource_service("oauth_clients").patch(ObjectId(_id), updates=updates)
app.cache.delete(_id)
return jsonify({"success": True}), 200
return jsonify(client), 200
updates = {}
updates["name"] = request_json.get("name")
await service.update(args.client_id, updates)
return Response({"success": True}, 200, ())


@blueprint.route("/oauth_clients/<_id>", methods=["DELETE"])
@clients_endpoints.endpoint("/oauth_clients/<string:client_id>", methods=["DELETE"])
@admin_only
def delete(_id):
async def delete(args: ClientArgs, params: None, request: Request) -> Response:
"""
Deletes the client with given client id
"""
get_resource_service("oauth_clients").delete_action(lookup={"_id": ObjectId(_id)})

app.cache.delete(_id)
return jsonify({"success": True}), 200
service = ClientService()
original = await service.find_by_id(args.client_id)

if not original:
raise NotFound(gettext("Client not found"))
try:
await service.delete(original)
except Exception as e:
return Response({"error": str(e)}, 400, ())
return Response({"success": True}, 200, ())
8 changes: 8 additions & 0 deletions newsroom/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from eve_elastic.elastic import parse_date, ElasticCursor
from flask import current_app as app, json, abort, request, g, flash, session, url_for
from flask_babel import gettext, format_date as _format_date
from superdesk.core.web import Request

from newsroom.types import PublicUserData, User, Company, Group, Permissions
from newsroom.template_filters import (
Expand Down Expand Up @@ -136,6 +137,13 @@ def get_json_or_400():
return data


async def get_json_or_400_async(req: Request):
data = await req.get_json()
if not isinstance(data, dict):
await req.abort(400)
return data


def get_type(type: Optional[str] = None) -> str:
item_type = type or request.args.get("type", "wire")
types: Dict[Union[str, None], str] = {
Expand Down
3 changes: 1 addition & 2 deletions newsroom/web/default_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@
"newsroom.settings",
"newsroom.news_api.api_tokens",
"newsroom.monitoring",
"newsroom.oauth_clients",
"newsroom.auth_server.oauth2",
"newsroom.company_admin",
]
Expand Down Expand Up @@ -178,7 +177,7 @@
"newsroom.notifications.send_scheduled_notifications",
]

MODULES = ["newsroom.ui_config_async"]
MODULES = ["newsroom.ui_config_async", "newsroom.oauth_clients"]

SITE_NAME = "Newshub"
COPYRIGHT_HOLDER = "Sourcefabric"
Expand Down

0 comments on commit aa65c21

Please sign in to comment.