Commit 01eb0dd1 authored by Luc Yriarte's avatar Luc Yriarte
Browse files

slb code push #4 / app only

parent 0318d3c5
......@@ -12,58 +12,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from cachetools import TTLCache
from fastapi import HTTPException, Depends
from fastapi import Depends, Request
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from jwt import PyJWTError
from starlette.authentication import AuthCredentials
from starlette.requests import Request
from starlette.status import HTTP_401_UNAUTHORIZED
from app.model.user import User
from app.utils import Context, async_with_cache
from app.helper import logger
import jwt
# manually using basic global cache for now as decorator doesn't work with coroutine
_user_info_cache = TTLCache(maxsize=512, ttl=60, getsizeof=lambda x: 1)
# Make the name very explicit for now
class OpenDESBearerToken(HTTPBearer):
pass
from app.utils import Context
security = OpenDESBearerToken()
security = HTTPBearer()
async def require_opendes_authorized_user(request: Request,
credentials: HTTPAuthorizationCredentials = Depends(security)):
token = credentials.credentials
ctx = Context.current()
user = await _get_user_from_token(ctx, token)
request.scope['auth'] = AuthCredentials(['authenticated'])
request.scope['user'] = user
Context.set_current_with_value(auth=token, user=user)
async def _get_user_from_token(ctx: Context, token: str) -> User:
global _user_info_cache
cache_key: str = token
return await async_with_cache(_user_info_cache, cache_key, get_user_from_token_not_cached, ctx, token)
async def get_user_from_token_not_cached(ctx: Context, token: str) -> User:
# TODO REAL entitlement call is needed here, for now basic decode without verify
try:
token_payload = jwt.decode(token, verify=False)
email = token_payload['email']
except (KeyError, PyJWTError):
raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED,
detail='invalid token',
headers={'WWW-Authenticate': 'Bearer'},
)
groups = []
return User(email=email, authenticated=True, groups=groups)
Context.set_current_with_value(auth=token)
......@@ -12,10 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import odes_entitlements
import odes_search
import odes_storage
from odes_entitlements.api_client import AsyncEntitlementsAuthAdministrationApi
from odes_search.api_client import AsyncSearchApi
from odes_storage.api_client import AsyncRecordsApi
from app.conf import Config
......@@ -23,16 +22,13 @@ from dataclasses import dataclass
from typing import Optional
__all__ = ['EntitlementsAuthServiceClient',
'SearchServiceClient',
__all__ = ['SearchServiceClient',
'StorageRecordServiceClient',
'make_entitlements_auth_client',
'make_search_client',
'make_storage_record_client']
from app.clients.clients_middleware import client_middleware
EntitlementsAuthServiceClient = AsyncEntitlementsAuthAdministrationApi
SearchServiceClient = AsyncSearchApi
StorageRecordServiceClient = AsyncRecordsApi
......@@ -44,19 +40,6 @@ class Limits:
keepalive_expiry: Optional[float] = 5.0
def make_entitlements_auth_client(host) -> EntitlementsAuthServiceClient:
entitlements_client = odes_entitlements.ApiClient(
host=host,
timeout=Config.de_client_config_timeout.value,
limits=Limits(
max_connections=Config.de_client_config_max_connection.value or None,
max_keepalive_connections=Config.de_client_config_max_keepalive.value or None)
)
entitlements_client.add_middleware(middleware=client_middleware)
return odes_entitlements.AsyncApis(entitlements_client).entitlements_auth_administration_api
def make_search_client(host) -> SearchServiceClient:
search_client = odes_search.ApiClient(
host=host,
......
......@@ -14,9 +14,9 @@
from asyncio import iscoroutinefunction, gather
import uuid
from fastapi import FastAPI, HTTPException
from fastapi import FastAPI, HTTPException, status
from osdu.core.api.storage.tenant import Tenant
from starlette import status
from odes_storage.models import *
from osdu.core.api.storage.blob_storage_base import BlobStorageBase
......
......@@ -104,11 +104,6 @@ class ConfigurationContainer:
factory=lambda x: x.lower()
)
service_host_entitlements: EnvVar = EnvVar(
key='SERVICE_HOST_ENTITLEMENTS',
description='Back-end for entitlements service',
is_mandatory=True)
service_host_search: EnvVar = EnvVar(
key='SERVICE_HOST_SEARCH',
description='Back-end for search service',
......@@ -128,13 +123,13 @@ class ConfigurationContainer:
de_client_config_max_connection: EnvVar = EnvVar(
key='DE_CLIENT_CFG_MAX_CONNECTION',
description='maximum number of allowable connections, 0 to always allow.',
default='200',
default='1000',
factory=lambda x: int(x))
de_client_config_max_keepalive: EnvVar = EnvVar(
key='DE_CLIENT_CFG_MAX_KEEPALIVE',
description='number of allowable keep-alive connections, 0 to always allow.',
default='200',
default='500',
factory=lambda x: int(x))
build_details: EnvVar = EnvVar(
......
......@@ -12,12 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from odes_entitlements.exceptions import (
ApiException as OSDUEntitlementsException,
UnexpectedResponse as OSDUEntitlementsUnexpectedResponse,
ResponseValidationError as OSDUEntitlementsResponseValidationError,
ResponseHandlingException as OSDUEntitlementsResponseHandlingException
)
import json
from typing import Dict
from odes_search.exceptions import (
ApiException as OSDUSearchException,
UnexpectedResponse as OSDUSearchUnexpectedResponse,
......@@ -40,10 +36,18 @@ from app.utils import get_ctx
OSDU_DATA_ECOSYSTEM_SEARCH = "osdu-data-ecosystem-search"
OSDU_DATA_ECOSYSTEM_STORAGE = "osdu-data-ecosystem-storage"
OSDU_DATA_ECOSYSTEM_ENTITLEMENTS = "osdu-data-ecosystem-entitlements"
OSDU_DATA_ECOSYSTEM_PARTITION = "osdu-data-ecosystem-partition"
CONTENT_ENCODING = "utf-16"
def load_content(content) -> Dict:
"""
returns dict from content whenever content is json or text
"""
try:
return json.loads(content)
except Exception:
return f"{content}"
async def http_search_error_handler(request: Request, exc: OSDUSearchException) -> JSONResponse:
......@@ -53,7 +57,7 @@ async def http_search_error_handler(request: Request, exc: OSDUSearchException)
get_ctx().logger.exception(f"http_search_error_handler - url: '{request.url}'")
if isinstance(exc, OSDUSearchUnexpectedResponse):
status = exc.status_code
errors = [exc.reason_phrase]
errors = [load_content(exc.content)]
elif isinstance(exc, OSDUSearchResponseValidationError):
status = exc.status_code
errors = exc.args
......@@ -72,12 +76,9 @@ async def http_storage_error_handler(request: Request, exc: OSDUStorageException
Catches and handles Exceptions raised by os-python-client
"""
get_ctx().logger.exception(f"http_storage_error_handler - url: '{request.url}'")
if isinstance(exc, OSDUStorageUnexpectedResponse):
if isinstance(exc, OSDUStorageUnexpectedResponse) or isinstance(exc, OSDUStorageResponseValidationError):
status = exc.status_code
errors = [exc.reason_phrase]
elif isinstance(exc, OSDUStorageResponseValidationError):
status = exc.status_code
errors = [exc.content]
errors = [load_content(exc.content)]
elif isinstance(exc, OSDUStorageResponseHandlingException):
status = HTTP_500_INTERNAL_SERVER_ERROR
errors = exc.source.args
......@@ -88,27 +89,6 @@ async def http_storage_error_handler(request: Request, exc: OSDUStorageException
return JSONResponse({"origin": OSDU_DATA_ECOSYSTEM_STORAGE, "errors": errors}, status_code=status)
async def http_entitlements_error_handler(request: Request, exc: OSDUEntitlementsException) -> JSONResponse:
"""
Catches and handles Exceptions raised by os-python-client
"""
get_ctx().logger.exception(f"http_entitlements_error_handler - url: '{request.url}'")
if isinstance(exc, OSDUEntitlementsUnexpectedResponse):
status = exc.status_code
errors = [exc.reason_phrase]
elif isinstance(exc, OSDUEntitlementsResponseValidationError):
status = exc.status_code
errors = [exc.content]
elif isinstance(exc, OSDUEntitlementsResponseHandlingException):
status = HTTP_500_INTERNAL_SERVER_ERROR
errors = exc.source.args
else:
status = HTTP_500_INTERNAL_SERVER_ERROR
errors = exc.args
return JSONResponse({"origin": OSDU_DATA_ECOSYSTEM_ENTITLEMENTS, "errors": errors}, status_code=status)
async def http_partition_error_handler(request: Request, exc: OSDUPartitionException) -> JSONResponse:
"""
Catches and handles Exceptions raised by os-python-client
......
......@@ -13,7 +13,6 @@
# limitations under the License.
from pydantic import ValidationError
from odes_entitlements.exceptions import ApiException as OSDUEntitlementsException
from odes_search.exceptions import ApiException as OSDUSearchException
from odes_storage.exceptions import ApiException as OSDUStorageException
from osdu_az.exceptions.data_access_error import DataAccessError as OSDUPartitionException
......@@ -23,7 +22,6 @@ from .validation_error import http422_error_handler
from .client_error import (
http_search_error_handler,
http_storage_error_handler,
http_entitlements_error_handler,
http_partition_error_handler
)
......@@ -35,6 +33,5 @@ def add_exception_handlers(app):
app.add_exception_handler(ValidationError, http422_error_handler)
app.add_exception_handler(OSDUSearchException, http_search_error_handler)
app.add_exception_handler(OSDUStorageException, http_storage_error_handler)
app.add_exception_handler(OSDUEntitlementsException, http_entitlements_error_handler)
app.add_exception_handler(OSDUPartitionException, http_partition_error_handler)
app.add_exception_handler(Exception, unhandled_error_handler)
from structlog.contextvars import bind_contextvars
from opencensus.trace.attributes_helper import COMMON_ATTRIBUTES
from starlette.requests import Request
from fastapi import Request
import http
......
......@@ -65,7 +65,7 @@ class Kind(str, Enum):
DateTime = 'DateTime'
class MetaItem(DDMSBaseModel):
class MetaItem(DDMSBaseModelWithExtra):
kind: Kind = Field(
...,
description='The kind of reference, unit, measurement, CRS or azimuth reference.',
......
This diff is collapsed.
[
{
"acl": {
"viewers": [
"data.default.viewers@{{datapartitionid}}.{{domain}}.com"
],
"owners": [
"data.default.owners@{{datapartitionid}}.{{domain}}.com"
]
},
"data": {
"classification": "Externally Processed LogSet",
"name": "myDipSet",
"relationships": {
"well": {
"confidence": 1.0,
"id": "{{datapartitionid}}:well:myWell",
"name": "myWell"
},
"wellbore": {
"confidence": 1.0,
"id": "{{datapartitionid}}:wellbore:myWellbore",
"name": "myWellbore"
}
}
},
"id": "{{datapartitionid}}:dipSet:myDipSet",
"kind": "{{datapartitionid}}:wks:dipSet:0.0.1",
"legal": {
"legaltags": [
"{{legaltags}}"
],
"otherRelevantDataCountries": [
"FR",
"US"
],
"status": "compliant"
}
}]
\ No newline at end of file
[
{
"acl": {
"viewers": [
"data.default.viewers@{{datapartitionid}}.{{domain}}.com"
],
"owners": [
"data.default.owners@{{datapartitionid}}.{{domain}}.com"
]
},
"data": {
"name": "myLogset",
"channelMnemonics": [
"GR",
"NPOR",
"RHOB"
],
"channelNames": [
"Gamma Ray",
"Neutron Porosity",
"Bulk Density"
],
"relationships": {
"well": {
"confidence": 1.0,
"id": "{{datapartitionid}}:well:myWell",
"name": "myWell"
},
"wellbore": {
"confidence": 1.0,
"id": "{{datapartitionid}}:wellbore:myWellbore",
"name": "myWellbore"
}
},
"elevationReference": {
"elevationFromMsl": {
"unitKey": "ft",
"value": 2680.5
},
"name": "KB"
},
"reference": {
"dataType": "number",
"dimension": 1,
"family": "Measured Depth",
"familyType": "Depth",
"format": "float32",
"mnemonic": "MD",
"name": "Measured Depth",
"unitKey": "ft"
},
"referenceType": "Measured Depth"
},
"id": "{{datapartitionid}}:logSet:myLogset",
"kind": "{{datapartitionid}}:wks:logSet:1.0.5",
"legal": {
"legaltags": [
"{{legaltags}}"
],
"otherRelevantDataCountries": [
"FR",
"US"
],
"status": "compliant"
}
}]
\ No newline at end of file
[
{
"acl": {
"viewers": [
"data.default.viewers@{{datapartitionid}}.{{domain}}.com"
],
"owners": [
"data.default.owners@{{datapartitionid}}.{{domain}}.com"
]
},
"data": {
"name": "myLog_GR",
"history": [{
"date": "2019-02-01T11:16:03Z",
"description": "Fake example",
"user": "BWillis"
}
],
"log": {
"dataType": "number",
"dimension": 20,
"family": "Gamma Ray",
"familyType": "Gamma Ray",
"format": "float32",
"mnemonic": "GR",
"name": "myLog_GR",
"properties": [{
"description": "Linear depth offset of the channel sensor relative to some reference point, typically the toolstring zero",
"name": "MEASURE_POINT_OFFSET",
"unitKey": "m",
"value": 0.264922
}
],
"unitKey": "gAPI"
},
"reference": {
"dataType": "number",
"dimension": 1,
"family": "Measured Depth",
"familyType": "Depth",
"format": "float32",
"mnemonic": "MD",
"name": "Measured Depth",
"unitKey": "ft"
},
"referenceType": "Measured Depth",
"relationships": {
"logSet": {
"confidence": 1.0,
"id": "{{datapartitionid}}:logSet:myLogset",
"name": "myLogset"
},
"well": {
"confidence": 1.0,
"id": "{{datapartitionid}}:well:myWell",
"name": "myWell"
},
"wellbore": {
"confidence": 1.0,
"id": "{{datapartitionid}}:wellbore:myWellbore",
"name": "myWellbore"
}
}
},
"id": "{{datapartitionid}}:log:myLog_GR",
"kind": "{{datapartitionid}}:wks:log:1.0.5",
"legal": {
"legaltags": [
"{{legaltags}}"
],
"otherRelevantDataCountries": [
"FR",
"US"
],
"status": "compliant"
}
}]
\ No newline at end of file
[
{
"acl": {
"viewers": [
"data.default.viewers@{{datapartitionid}}.{{domain}}.com"
],
"owners": [
"data.default.owners@{{datapartitionid}}.{{domain}}.com"
]
},
"data": {
"depth": {
"unitKey": "ft",
"value": 150
},
"md": {
"unitKey": "ft",
"value": 150
},
"tvd": {
"unitKey": "ft",
"value": 200
},
"name": "myMarker",
"relationships": {
"well": {
"confidence": 1.0,
"id": "{{datapartitionid}}:well:myWell",
"name": "myWell"
},
"wellbore": {
"confidence": 1.0,
"id": "{{datapartitionid}}:wellbore:myWellbore",
"name": "myWellbore"
}
}
},
"id": "{{datapartitionid}}:marker:myMarker",
"kind": "{{datapartitionid}}:wks:marker:1.0.4",
"legal": {
"legaltags": [
"{{legaltags}}"
],
"otherRelevantDataCountries": [
"FR",
"US"
],
"status": "compliant"
}
}
]
\ No newline at end of file
[
{
"acl": {
"viewers": [
"data.default.viewers@{{datapartitionid}}.{{domain}}.com"
],
"owners": [
"data.default.owners@{{datapartitionid}}.{{domain}}.com"
]
},
"data": {
"azimuthReference": "string",
"channelMnemonics": [
"Measured Depth",
"True Vertical Depth",
"XOffset",
"YOffset"
],
"channelNames": [
"MD",
"TVD",
"X",
"Y"
],
"channels": [
{
"dimension": 1,
"family": "Measured Depth",
"name": "MD",
"unitKey": "ft"
},
{
"dimension": 1,
"family": "True Vertical Depth",
"name": "TVD",
"unitKey": "ft"
},
{
"dimension": 1,
"family": "XOffset",
"name": "X",
"unitKey": "ft"
},
{
"dimension": 1,
"family": "YOffset",
"name": "Y",
"unitKey": "ft"
}
],
"name": "myTrajectory",
"relationships": {
"well": {
"confidence": 1.0,
"id": "{{datapartitionid}}:well:myWell",
"name": "myWell"
},
"wellbore": {
"confidence": 1.0,
"id": "{{datapartitionid}}:wellbore:myWellbore",
"name": "myWellbore"
}
}
},
"id": "{{datapartitionid}}:trajectory:myTrajectory",
"kind": "{{datapartitionid}}:wks:trajectory:1.0.5",
"legal": {
"legaltags": [
"{{legaltags}}"
],
"otherRelevantDataCountries": [
"FR",
"US"
],
"status": "compliant"
}
}
]
\ No newline at end of file
<