Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions foca/database/register_mongodb.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,18 +123,24 @@ def _create_mongo_client(
Returns:
MongoDB client for Flask application instance.
"""
# Normalize environment variables: strip whitespace and treat empty strings as None
mongo_username = os.environ.get('MONGO_USERNAME', '').strip() or None
mongo_password = os.environ.get('MONGO_PASSWORD', '').strip() or None
mongo_host = os.environ.get('MONGO_HOST', '').strip() or host
mongo_port = os.environ.get('MONGO_PORT', '').strip() or port
mongo_dbname = os.environ.get('MONGO_DBNAME', '').strip() or db

auth = ''
user = os.environ.get('MONGO_USERNAME')
if user is not None and user != "":
if mongo_username is not None and mongo_password is not None:
auth = '{username}:{password}@'.format(
username=os.environ.get('MONGO_USERNAME'),
password=os.environ.get('MONGO_PASSWORD'),
username=mongo_username,
password=mongo_password,
)

app.config['MONGO_URI'] = 'mongodb://{auth}{host}:{port}/{db}'.format(
host=os.environ.get('MONGO_HOST', host),
port=os.environ.get('MONGO_PORT', port),
db=os.environ.get('MONGO_DBNAME', db),
host=mongo_host,
port=mongo_port,
db=mongo_dbname,
auth=auth
)

Expand All @@ -144,9 +150,9 @@ def _create_mongo_client(
"Registered database '{db}' at URI '{host}':'{port}' with Flask "
'application.'
).format(
db=os.environ.get('MONGO_DBNAME', db),
host=os.environ.get('MONGO_HOST', host),
port=os.environ.get('MONGO_PORT', port)
db=mongo_dbname,
host=mongo_host,
port=mongo_port
)
)
return mongo
1 change: 1 addition & 0 deletions foca/foca.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ def create_app(self) -> App:
mongo_config=self.conf.db,
access_control_config=self.conf.security.access_control,
)
logger.info("Access control registered.")
else:
if (
self.conf.security.access_control.api_specs
Expand Down
8 changes: 4 additions & 4 deletions foca/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from enum import Enum
from functools import reduce
import importlib
from importlib.resources import path as resource_path
from importlib.resources import as_file, files
import operator
from pathlib import Path
from typing import (
Expand Down Expand Up @@ -726,10 +726,10 @@ def validate_model_path(cls, v: Optional[str]) -> str:

"""
if v is None:
with resource_path(
ACCESS_CONTROL_BASE_PATH,
model_file = files(ACCESS_CONTROL_BASE_PATH).joinpath(
DEFAULT_MODEL_FILE
) as _path:
)
with as_file(model_file) as _path:
return str(_path)

model_path = Path(v)
Expand Down
11 changes: 7 additions & 4 deletions foca/security/access_control/register_access_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import logging
from functools import wraps
from importlib.resources import path as resource_path
from importlib.resources import as_file, files
from pathlib import Path
from typing import (Callable, Optional, Tuple)

Expand Down Expand Up @@ -82,11 +82,13 @@ def register_access_control(
mongo_config=mongo_config,
access_control_config=access_control_config
)
logger.info("Access control enforcer registered.")

cnx_app = register_permission_specs(
app=cnx_app,
access_control_config=access_control_config
)
logger.info("Access control permission specifications registered.")

return cnx_app

Expand All @@ -108,9 +110,10 @@ def register_permission_specs(
"""
# Check if default, get package path variables for specs.
if access_control_config.api_specs is None:
with resource_path(
ACCESS_CONTROL_BASE_PATH, DEFAULT_API_SPEC_PATH
) as _path:
spec_file = files(ACCESS_CONTROL_BASE_PATH).joinpath(
DEFAULT_API_SPEC_PATH
)
with as_file(spec_file) as _path:
spec_path = str(_path)
else:
spec_path = access_control_config.api_specs
Expand Down
7 changes: 7 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,10 @@ version_variable = foca/__init__.py:__version__

[mypy]
ignore_missing_imports = True

[tool:pytest]
filterwarnings =
ignore:Accessing jsonschema\.draft4_format_checker is deprecated:DeprecationWarning
ignore:jsonschema\.RefResolver is deprecated:DeprecationWarning
ignore:jsonschema\.exceptions\.RefResolutionError is deprecated:DeprecationWarning
ignore:Passing a schema to Validator\.iter_errors is deprecated:DeprecationWarning
39 changes: 38 additions & 1 deletion tests/security/access_control/test_register_access_control.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
"""Tests for registering access control"""

import logging
from types import SimpleNamespace
from flask import Flask
import mongomock
from pymongo import MongoClient
from unittest import TestCase
import pytest

from foca.security.access_control.register_access_control import (
check_permissions
check_permissions,
register_access_control,
)
from foca.security.access_control.foca_casbin_adapter.adapter import Adapter
from foca.errors.exceptions import Forbidden
Expand Down Expand Up @@ -110,3 +113,37 @@ def mock_func():
):
with pytest.raises(Forbidden):
mock_func()


def test_register_access_control_logs_setup_steps(monkeypatch, caplog):
"""Test setup logs for access control registration flow."""
caplog.set_level(logging.INFO)
access_control = AccessControlConfig(**ACCESS_CONTROL_CONFIG)
mongo_config = MongoConfig(**MONGO_CONFIG)

dummy_app = SimpleNamespace(
app=SimpleNamespace(config=SimpleNamespace(foca=SimpleNamespace(db=None)))
)

monkeypatch.setattr(
"foca.security.access_control.register_access_control.add_new_database",
lambda app, conf, db_conf, db_name: None,
)
monkeypatch.setattr(
"foca.security.access_control.register_access_control.register_casbin_enforcer",
lambda app, mongo_config, access_control_config: app,
)
monkeypatch.setattr(
"foca.security.access_control.register_access_control.register_permission_specs",
lambda app, access_control_config: app,
)

updated_app = register_access_control(
cnx_app=dummy_app,
mongo_config=mongo_config,
access_control_config=access_control,
)

assert updated_app is dummy_app
assert "Access control enforcer registered." in caplog.text
assert "Access control permission specifications registered." in caplog.text
11 changes: 9 additions & 2 deletions tests/test_foca.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,22 +130,29 @@ def test_foca_CORS_disabled():
assert app.app.config.foca.security.cors.enabled is False


def test_foca_invalid_access_control():
def test_foca_invalid_access_control(capsys):
"""Ensures access control is not enabled if auth flag is disabled."""
foca = Foca(config_file=INVALID_ACCESS_CONTROL_CONF)
app = foca.create_app()
logs = capsys.readouterr().err
assert app.app.config.foca.db is None
assert "Please enable security config to register access control." in logs
assert "Access control registered." not in logs


def test_foca_valid_access_control():
def test_foca_valid_access_control(capsys):
"""Ensures access control settings are set correctly."""
foca = Foca(config_file=VALID_ACCESS_CONTROL_CONF)
app = foca.create_app()
logs = capsys.readouterr().err
my_db = app.app.config.foca.db.dbs["test_db"]
my_coll = my_db.collections["test_collection"]
assert isinstance(my_db.client, Database)
assert isinstance(my_coll.client, Collection)
assert isinstance(app, App)
assert "Access control enforcer registered." in logs
assert "Access control permission specifications registered." in logs
assert "Access control registered." in logs


def test_foca_create_celery_app():
Expand Down