Skip to content

Commit

Permalink
🎨 Synced retention policy and minor template changes (ITISFoundation#…
Browse files Browse the repository at this point in the history
  • Loading branch information
pcrespov authored Mar 15, 2024
1 parent b3df99f commit de9816c
Show file tree
Hide file tree
Showing 20 changed files with 75 additions and 38 deletions.
1 change: 1 addition & 0 deletions .env-devel
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ WEBSERVER_GARBAGE_COLLECTOR=null
WEBSERVER_DB_LISTENER=0

WEBSERVER_LOGIN={}
LOGIN_ACCOUNT_DELETION_RETENTION_DAYS=31
LOGIN_REGISTRATION_CONFIRMATION_REQUIRED=0
LOGIN_REGISTRATION_INVITATION_REQUIRED=0
LOGIN_2FA_REQUIRED=0
Expand Down
1 change: 1 addition & 0 deletions services/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,7 @@ services:
INVITATIONS_USERNAME: ${INVITATIONS_USERNAME}

WEBSERVER_LOGIN: ${WEBSERVER_LOGIN}
LOGIN_ACCOUNT_DELETION_RETENTION_DAYS: ${LOGIN_ACCOUNT_DELETION_RETENTION_DAYS}
LOGIN_REGISTRATION_CONFIRMATION_REQUIRED: ${LOGIN_REGISTRATION_CONFIRMATION_REQUIRED}
LOGIN_REGISTRATION_INVITATION_REQUIRED: ${LOGIN_REGISTRATION_INVITATION_REQUIRED}
LOGIN_2FA_REQUIRED: ${LOGIN_2FA_REQUIRED}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ qx.Class.define("osparc.desktop.credits.DeleteAccount", {
switch (id) {
case "intro-text": {
const supportEmail = osparc.store.VendorInfo.getInstance().getSupportEmail();
const retentionDays = osparc.store.StaticInfo.getInstance().getAccountDeletionRetentionDays();
const text = this.tr(`\
This account will be deleted in 14 days.<br>\
This account will be <strong>deleted in ${retentionDays} days</strong>.<br>\
During this period, if you want to recover it or delete your\
data right away, please send us an email to ${supportEmail}.\
data right away, please send us an email to <a href="mailto:${supportEmail}">${supportEmail}</a>.\
`);
control = new qx.ui.basic.Label().set({
value: text,
Expand Down Expand Up @@ -108,9 +109,11 @@ qx.Class.define("osparc.desktop.credits.DeleteAccount", {
password: form.getItem("password").getValue()
}
};
const retentionDays = osparc.store.StaticInfo.getInstance().getAccountDeletionRetentionDays();

osparc.data.Resources.fetch("auth", "unregister", params)
.then(() => {
const msg = this.tr("You account will be deleted in 14 days");
const msg = this.tr(`You account will be deleted in ${retentionDays} days`);
osparc.FlashMessenger.getInstance().logAs(msg, "INFO");
this.fireEvent("deleted");
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ qx.Class.define("osparc.store.StaticInfo", {
return wsStaticData[key];
}
return null;
},

getAccountDeletionRetentionDays: function() {
const staticKey = "webserverLogin";
const wsStaticData = this.getValue(staticKey);
const key = "LOGIN_ACCOUNT_DELETION_RETENTION_DAYS";
if (key in wsStaticData) {
return wsStaticData[key];
}
return 30;
}
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from .activity.plugin import setup_activity
from .announcements.plugin import setup_announcements
from .api_keys.plugin import setup_api_keys
from .application_settings import get_settings, setup_settings
from .application_settings import get_application_settings, setup_settings
from .catalog.plugin import setup_catalog
from .clusters.plugin import setup_clusters
from .db.plugin import setup_db
Expand Down Expand Up @@ -55,7 +55,7 @@


async def _welcome_banner(app: web.Application):
settings = get_settings(app)
settings = get_application_settings(app)
print(WELCOME_MSG, flush=True) # noqa: T201
if settings.WEBSERVER_GARBAGE_COLLECTOR:
print("with", WELCOME_GC_MSG, flush=True) # noqa: T201
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ def to_client_statics(self) -> dict[str, Any]:
"SIMCORE_VCS_RELEASE_URL": True,
"SWARM_STACK_NAME": True,
"WEBSERVER_PROJECTS": {"PROJECTS_MAX_NUM_RUNNING_DYNAMIC_NODES"},
"WEBSERVER_LOGIN": {"LOGIN_ACCOUNT_DELETION_RETENTION_DAYS"},
},
exclude_none=True,
)
Expand All @@ -406,7 +407,7 @@ def setup_settings(app: web.Application) -> ApplicationSettings:
return settings


def get_settings(app: web.Application) -> ApplicationSettings:
def get_application_settings(app: web.Application) -> ApplicationSettings:
settings: ApplicationSettings = app[APP_SETTINGS_KEY]
assert settings, "Forgot to setup plugin?" # nosec
return settings
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from servicelib.aiohttp.typing_extension import Handler

from ._constants import MSG_UNDER_DEVELOPMENT
from .application_settings import ApplicationSettings, get_settings
from .application_settings import ApplicationSettings, get_application_settings

_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -326,7 +326,7 @@ def _set_if_disabled(field_name, section):
def requires_dev_feature_enabled(handler: Handler):
@functools.wraps(handler)
async def _handler_under_dev(request: web.Request):
app_settings = get_settings(request.app)
app_settings = get_application_settings(request.app)
if not app_settings.WEBSERVER_DEV_FEATURES_ENABLED:
raise NotImplementedError(MSG_UNDER_DEVELOPMENT)
return await handler(request)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from models_library.wallets import ZERO_CREDITS, WalletID, WalletInfo
from pydantic import parse_obj_as

from ..application_settings import get_settings
from ..application_settings import get_application_settings
from ..products.api import Product
from ..projects import api as projects_api
from ..users import preferences_api as user_preferences_api
Expand All @@ -21,7 +21,7 @@ async def get_wallet_info(
project_id: ProjectID,
product_name: str,
) -> WalletInfo | None:
app_settings = get_settings(app)
app_settings = get_application_settings(app)
if not (
product.is_payment_enabled and app_settings.WEBSERVER_CREDIT_COMPUTATION_ENABLED
):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
async def send_close_account_email(
request: web.Request,
user_email: EmailStr,
user_name: str,
retention_days: PositiveInt = 30,
user_first_name: str,
retention_days: PositiveInt,
):
template_name = "close_account.jinja2"
email_template_path = await get_product_template_path(request, template_name)
Expand All @@ -30,7 +30,7 @@ async def send_close_account_email(
template=email_template_path,
context={
"host": request.host,
"name": user_name.capitalize(),
"name": user_first_name.capitalize(),
"support_email": product.support_email,
"retention_days": retention_days,
"product": product,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from .._constants import RQ_PRODUCT_KEY
from .._meta import API_VTAG
from ..products.api import get_current_product
from ..products.api import Product, get_current_product
from ..security.api import check_password, forget_identity
from ..security.decorators import permission_required
from ..users.api import get_user_credentials, set_user_as_deleted
Expand All @@ -28,6 +28,7 @@
send_close_account_email,
)
from .decorators import login_required
from .settings import LoginSettingsForProduct, get_plugin_settings
from .utils import flash_response, notify_user_logout

_logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -87,6 +88,11 @@ async def unregister_account(request: web.Request):
req_ctx = _AuthenticatedContext.parse_obj(request)
body = await parse_request_body_as(UnregisterCheck, request)

product: Product = get_current_product(request)
settings: LoginSettingsForProduct = get_plugin_settings(
request.app, product_name=product.name
)

# checks before deleting
credentials = await get_user_credentials(request.app, user_id=req_ctx.user_id)
if body.email != credentials.email.lower() or not check_password(
Expand Down Expand Up @@ -118,8 +124,8 @@ async def unregister_account(request: web.Request):
send_close_account_email(
request,
user_email=credentials.email,
user_name=credentials.display_name,
retention_days=30,
user_first_name=credentials.display_name,
retention_days=settings.LOGIN_ACCOUNT_DELETION_RETENTION_DAYS,
),
task_suffix_name=f"{__name__}.unregister_account.send_close_account_email",
fire_and_forget_tasks_collection=request.app[APP_FIRE_AND_FORGET_TASKS_KEY],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ async def register(request: web.Request):
context={
"host": request.host,
"link": email_confirmation_url, # SEE email_confirmation handler (action=REGISTRATION)
"name": user["name"],
"name": user.get("first_name") or user["name"],
"support_email": product.support_email,
"product": product,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@


class LoginSettings(BaseCustomSettings):
LOGIN_ACCOUNT_DELETION_RETENTION_DAYS: PositiveInt = Field(
default=30,
description="Retention time (in days) of all the data after a user has requested the deletion of their account"
"NOTE: exposed to the front-end as `to_client_statics`",
)

LOGIN_REGISTRATION_CONFIRMATION_REQUIRED: bool = Field(
default=True,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
)
from simcore_postgres_database.webserver_models import ProjectType as ProjectTypeDB

from ..application_settings import get_settings
from ..application_settings import get_application_settings
from ..catalog import client as catalog_client
from ..director_v2 import api
from ..storage.api import (
Expand Down Expand Up @@ -67,7 +67,7 @@ async def _prepare_project_copy(
project_uuid=f"{src_project_uuid}",
user_id=user_id,
)
settings = get_settings(app).WEBSERVER_PROJECTS
settings = get_application_settings(app).WEBSERVER_PROJECTS
assert settings # nosec
if max_bytes := settings.PROJECTS_MAX_COPY_SIZE_BYTES:
# get project total data size
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
)
from servicelib.utils import logged_gather

from ..application_settings import get_settings
from ..application_settings import get_application_settings
from ..storage.api import get_download_link, get_files_in_node_folder
from .exceptions import ProjectStartsTooManyDynamicNodesError

Expand All @@ -51,7 +51,7 @@ def check_num_service_per_projects_limit(
"""
raises ProjectStartsTooManyDynamicNodes if the user cannot start more services
"""
project_settings = get_settings(app).WEBSERVER_PROJECTS
project_settings = get_application_settings(app).WEBSERVER_PROJECTS
assert project_settings # nosec
if project_settings.PROJECTS_MAX_NUM_RUNNING_DYNAMIC_NODES > 0 and (
number_of_services >= project_settings.PROJECTS_MAX_NUM_RUNNING_DYNAMIC_NODES
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
)
from simcore_postgres_database.webserver_models import ProjectType

from ..application_settings import get_settings
from ..application_settings import get_application_settings
from ..catalog import client as catalog_client
from ..director_v2 import api as director_v2_api
from ..dynamic_scheduler import api as dynamic_scheduler_api
Expand Down Expand Up @@ -441,7 +441,7 @@ async def _start_dynamic_service(
# Get wallet/pricing/hardware information
wallet_info, pricing_info, hardware_info = None, None, None
product = products_api.get_current_product(request)
app_settings = get_settings(request.app)
app_settings = get_application_settings(request.app)
if (
product.is_payment_enabled
and app_settings.WEBSERVER_CREDIT_COMPUTATION_ENABLED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
from tenacity.wait import wait_fixed
from yarl import URL

from .._constants import APP_PRODUCTS_KEY, APP_SETTINGS_KEY
from .._constants import APP_PRODUCTS_KEY
from ..application_settings import ApplicationSettings, get_application_settings
from ..products.api import Product
from ._constants import (
APP_FRONTEND_CACHED_INDEXES_KEY,
Expand Down Expand Up @@ -94,12 +95,13 @@ async def create_statics_json(app: web.Application) -> None:
# on_startup instead of upon setup

# Adds general server settings
app_settings = app[APP_SETTINGS_KEY]
app_settings: ApplicationSettings = get_application_settings(app)
common: dict = app_settings.to_client_statics()

# Adds specifics to front-end app
frontend_settings: FrontEndAppSettings = app_settings.WEBSERVER_FRONTEND
common.update(frontend_settings.to_statics())
frontend_settings: FrontEndAppSettings | None = app_settings.WEBSERVER_FRONTEND
if frontend_settings:
common.update(frontend_settings.to_statics())

# Adds products defined in db
products: dict[str, Product] = app[APP_PRODUCTS_KEY]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def test_settings_to_client_statics(app_settings: ApplicationSettings):
# all key in camelcase
assert all(
key[0] == key[0].lower() and "_" not in key and key.lower() != key
for key in statics.keys()
for key in statics
), f"Got {list(statics.keys())}"

# special alias
Expand All @@ -218,12 +218,19 @@ def test_settings_to_client_statics_plugins(

print("STATICS:\n", json_dumps(statics, indent=1))

assert settings.WEBSERVER_LOGIN

assert (
statics["webserverLogin"]["LOGIN_ACCOUNT_DELETION_RETENTION_DAYS"]
== settings.WEBSERVER_LOGIN.LOGIN_ACCOUNT_DELETION_RETENTION_DAYS
)

assert set(statics["pluginsDisabled"]) == (disable_plugins | {"WEBSERVER_CLUSTERS"})


def test_avoid_sensitive_info_in_public(app_settings: ApplicationSettings):
# avoids display of sensitive info
assert not any("pass" in key for key in app_settings.public_dict().keys())
assert not any("token" in key for key in app_settings.public_dict().keys())
assert not any("secret" in key for key in app_settings.public_dict().keys())
assert not any("private" in key for key in app_settings.public_dict().keys())
assert not any("pass" in key for key in app_settings.public_dict())
assert not any("token" in key for key in app_settings.public_dict())
assert not any("secret" in key for key in app_settings.public_dict())
assert not any("private" in key for key in app_settings.public_dict())
4 changes: 2 additions & 2 deletions services/web/server/tests/unit/with_dbs/02/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from pytest_simcore.helpers.utils_login import UserInfoDict
from pytest_simcore.helpers.utils_projects import NewProject, delete_all_projects
from settings_library.catalog import CatalogSettings
from simcore_service_webserver.application_settings import get_settings
from simcore_service_webserver.application_settings import get_application_settings
from simcore_service_webserver.catalog.settings import get_plugin_settings
from simcore_service_webserver.projects.models import ProjectDict

Expand Down Expand Up @@ -282,7 +282,7 @@ def disable_max_number_of_running_dynamic_nodes(
@pytest.fixture
def max_amount_of_auto_started_dyn_services(client: TestClient) -> int:
assert client.app
projects_settings = get_settings(client.app).WEBSERVER_PROJECTS
projects_settings = get_application_settings(client.app).WEBSERVER_PROJECTS
assert projects_settings
return projects_settings.PROJECTS_MAX_NUM_RUNNING_DYNAMIC_NODES

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from servicelib.aiohttp.long_running_tasks.server import TaskGet, TaskProgress
from simcore_postgres_database.models.users import UserRole
from simcore_service_webserver._meta import api_version_prefix
from simcore_service_webserver.application_settings import get_settings
from simcore_service_webserver.application_settings import get_application_settings
from simcore_service_webserver.projects.models import ProjectDict
from tenacity._asyncio import AsyncRetrying
from tenacity.stop import stop_after_delay
Expand Down Expand Up @@ -285,7 +285,7 @@ async def test_copying_too_large_project_returns_422(
request_create_project: Callable[..., Awaitable[ProjectDict]],
):
assert client.app
app_settings = get_settings(client.app)
app_settings = get_application_settings(client.app)
large_project_total_size = (
app_settings.WEBSERVER_PROJECTS.PROJECTS_MAX_COPY_SIZE_BYTES + 1
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from pytest_simcore.helpers.typing_env import EnvVarsDict
from pytest_simcore.helpers.utils_envs import setenvs_from_dict
from simcore_service_webserver.application import create_application
from simcore_service_webserver.application_settings import get_settings
from simcore_service_webserver.application_settings import get_application_settings


class Entrypoint(NamedTuple):
Expand Down Expand Up @@ -58,7 +58,7 @@ def app(app_environment: EnvVarsDict) -> web.Application:
# - all plugins are setup but app is NOT started (i.e events are not triggered)
#
app_ = create_application()
print(get_settings(app_).json(indent=1))
print(get_application_settings(app_).json(indent=1))
return app_


Expand Down

0 comments on commit de9816c

Please sign in to comment.