-
Notifications
You must be signed in to change notification settings - Fork 83
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[SDESK-7484] Create async Content API Subscriber Token and Auth (#2825)
* Add HATEOAS links for async REST API to eve homepoint * Add cors headers to Resource REST API * Implement `content_api` token resource and service SDESK-7484 * Implement async TokenAuth for `ContentApi` SDESK-7484 * Small fixes SDESK-7484 * Fix black * Add mock sync auth to avoid breaking the app SDESK-7484 * Fix content_api.items which depends on legacy auth SDESK-7484 * Move related links generation to ResourceModel Move related links generation from REST endpoints to ResourceModel for better encapsulation and reusability. Cache relationships at class creation time to avoid potential performance issues because of the extraction of annotations. Add utilities for consistent URL generation and annotation handling across class hierarchies. * Fix `mypy` complains. Some type errors ignored Added some TODO-ASYNC comments to some ignored type errors for now * Fix flake8 complains SDESK-7484 * Split `resource_endpoints_test.py` as it is getting too big SDESK-7484 * Fix broken tests SDESK-7484 * Rename from `nose-tests.yml` to `tests.yml` simply We no longer use `nose` so there is not reason to have the file name with it * Fix content_api tests SDESK-7484 * Add tests for related items in hateoas SDESK-7484 --------- Co-authored-by: Mark Pittaway <[email protected]>
- Loading branch information
1 parent
c39fc7c
commit 5bf9eff
Showing
30 changed files
with
731 additions
and
274 deletions.
There are no files selected for viewing
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
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 |
---|---|---|
@@ -1,48 +1,4 @@ | ||
# -*- coding: utf-8; -*- | ||
# | ||
# This file is part of Superdesk. | ||
# | ||
# Copyright 2013, 2014, 2015 Sourcefabric z.u. and contributors. | ||
# | ||
# For the full copyright and license information, please see the | ||
# AUTHORS and LICENSE files distributed with this source code, or | ||
# at https://www.sourcefabric.org/superdesk/license | ||
from .resource import CompanyTokenResource | ||
from .service import CompanyTokenService | ||
|
||
import superdesk | ||
|
||
from eve.auth import TokenAuth | ||
|
||
from superdesk.core import get_current_app | ||
from superdesk.flask import g | ||
from superdesk.utc import utcnow | ||
from superdesk.publish.subscriber_token import SubscriberTokenResource, SubscriberTokenService | ||
|
||
from content_api.tokens.resource import CompanyTokenResource # noqa | ||
from content_api.tokens.service import CompanyTokenService # noqa | ||
|
||
|
||
TOKEN_RESOURCE = "subscriber_token" | ||
|
||
|
||
class AuthSubscriberTokenResource(SubscriberTokenResource): | ||
item_methods = [] | ||
resource_methods = [] | ||
|
||
|
||
class SubscriberTokenAuth(TokenAuth): | ||
def check_auth(self, token, allowed_roles, resource, method): | ||
"""Try to find auth token and if valid put subscriber id into ``g.user``.""" | ||
app = get_current_app() | ||
data = app.data.mongo.find_one(TOKEN_RESOURCE, req=None, _id=token) | ||
if not data: | ||
return False | ||
now = utcnow() | ||
if data.get("expiry") and data.get("expiry") < now: | ||
app.data.mongo.remove(TOKEN_RESOURCE, {"_id": token}) | ||
return False | ||
g.user = str(data.get("subscriber")) | ||
return g.user | ||
|
||
|
||
def init_app(app) -> None: | ||
superdesk.register_resource(TOKEN_RESOURCE, AuthSubscriberTokenResource, SubscriberTokenService, _app=app) | ||
__all__ = ["CompanyTokenResource", "CompanyTokenService"] |
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,90 @@ | ||
from eve.auth import TokenAuth | ||
|
||
from superdesk.utc import utcnow | ||
from superdesk.errors import SuperdeskApiError | ||
from superdesk.core.types.web import AuthRule, Request | ||
from superdesk.core.auth.user_auth import UserAuthProtocol | ||
from superdesk.core.auth.rules import endpoint_intrinsic_auth_rule | ||
from superdesk.publish.subscriber_token import SubscriberTokenService, SubscriberToken | ||
|
||
|
||
# TODO-ASYNC: Needed to avoid the content_api items endpoint from crashing | ||
# as it relies on the `user` stored in the `g` object. Once items are migrated | ||
# we should remove this | ||
class LegacyTokenAuth(TokenAuth): | ||
def check_auth(self, token, allowed_roles, resource, method): | ||
"""Try to find auth token and if valid put subscriber id into ``g.user``.""" | ||
from superdesk.flask import g | ||
|
||
data = SubscriberTokenService().mongo.find_one(token) | ||
if not data: | ||
return False | ||
now = utcnow() | ||
if data.get("expiry") and data.get("expiry") < now: | ||
SubscriberTokenService().mongo.delete_one({"_id": token}) | ||
return False | ||
g.user = str(data.get("subscriber")) | ||
return g.user | ||
|
||
|
||
class SubscriberTokenAuth(UserAuthProtocol): | ||
def get_default_auth_rules(self) -> list[AuthRule]: | ||
return [endpoint_intrinsic_auth_rule] | ||
|
||
def get_token_from_request(self, request: Request) -> str | None: | ||
""" | ||
Extracts the token from `Authorization` header. Code taken partly | ||
from eve.Auth module | ||
""" | ||
|
||
auth = (request.get_header("Authorization") or "").strip() | ||
if len(auth): | ||
if auth.lower().startswith(("token", "bearer", "basic")): | ||
return auth.split(" ")[1] if " " in auth else None | ||
return auth | ||
|
||
return None | ||
|
||
async def authenticate(self, request: Request) -> None: | ||
""" | ||
Tries to find the auth token in the request and if valid put subscriber id into ``g.user``. | ||
""" | ||
token_service = SubscriberTokenService() | ||
token_missing_exception = SuperdeskApiError.forbiddenError(message="Authorization token missing.") | ||
token_id = self.get_token_from_request(request) | ||
|
||
if token_id is None: | ||
raise token_missing_exception | ||
|
||
token = await token_service.find_by_id(token_id) | ||
if token is None: | ||
await self.stop_session(request) | ||
raise token_missing_exception | ||
|
||
await self.check_token_validity(token) | ||
await self.start_session(request, token) | ||
|
||
async def check_token_validity(self, token: SubscriberToken) -> None: | ||
""" | ||
Checks if the token is valid and if it has expired. | ||
""" | ||
|
||
if token.expiry and token.expiry < utcnow(): | ||
await SubscriberTokenService().delete(token) | ||
raise SuperdeskApiError.forbiddenError(message="Authorization token expired.") | ||
|
||
async def start_session(self, request: Request, token: SubscriberToken) -> None: # type: ignore[override] | ||
""" | ||
Puts the subscriber id into ``g.user``. | ||
""" | ||
request.storage.request.set("user", str(token.subscriber)) | ||
|
||
async def stop_session(self, request: Request) -> None: | ||
""" | ||
Removes the subscriber id from ``g.user``. | ||
""" | ||
request.storage.request.set("user", None) | ||
|
||
def get_current_user(self, request: Request) -> str | None: | ||
"""Overrides as it is needed.""" | ||
return None |
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
Oops, something went wrong.