Skip to content

Commit 2b9bf2b

Browse files
authored
✨ Add wb-auth new Service and Redirect ForwardAuth for Vendor Services (ITISFoundation#8130)
1 parent 48fadf2 commit 2b9bf2b

File tree

11 files changed

+304
-38
lines changed

11 files changed

+304
-38
lines changed

.env-devel

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,11 +269,15 @@ VENDOR_DEV_MANUAL_IMAGE=containous/whoami
269269
VENDOR_DEV_MANUAL_REPLICAS=1
270270
VENDOR_DEV_MANUAL_SUBDOMAIN=manual
271271

272-
## VENDOR DEVELOPMENT SERVICES ---
272+
## WEBSERVER SERVICES VARIANTS ---
273273

274274
WB_API_WEBSERVER_HOST=wb-api-server
275275
WB_API_WEBSERVER_PORT=8080
276276

277+
WB_AUTH_WEBSERVER_HOST=wb-auth
278+
WB_AUTH_WEBSERVER_PORT=8080
279+
WB_AUTH_LOGLEVEL=INFO
280+
277281
WB_GC_ACTIVITY=null
278282
WB_GC_ANNOUNCEMENTS=0
279283
WB_GC_CATALOG=null

packages/pytest-simcore/src/pytest_simcore/environment_configs.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from typing import Any
1010

1111
import pytest
12+
from faker import Faker
1213

1314
from .helpers.monkeypatch_envs import load_dotenv, setenvs_from_dict
1415
from .helpers.typing_env import EnvVarsDict
@@ -112,7 +113,7 @@ def service_name(project_slug_dir: Path) -> str:
112113

113114

114115
@pytest.fixture(scope="session")
115-
def services_docker_compose_dict(services_docker_compose_file: Path) -> EnvVarsDict:
116+
def docker_compose_services_dict(services_docker_compose_file: Path) -> EnvVarsDict:
116117
# NOTE: By keeping import here, this library is ONLY required when the fixture is used
117118
import yaml
118119

@@ -121,22 +122,41 @@ def services_docker_compose_dict(services_docker_compose_file: Path) -> EnvVarsD
121122
return content
122123

123124

125+
@pytest.fixture
126+
def docker_compose_service_hostname(
127+
faker: Faker, service_name: str, docker_compose_services_dict: dict[str, Any]
128+
) -> str:
129+
"""Evaluates `hostname` from docker-compose service"""
130+
hostname_template = docker_compose_services_dict["services"][service_name][
131+
"hostname"
132+
]
133+
134+
# Generate fake values to replace Docker Swarm template variables
135+
node_hostname = faker.hostname(levels=1)
136+
task_slot = faker.random_int(min=0, max=10)
137+
138+
# Replace the Docker Swarm template variables with faker values
139+
return hostname_template.replace("{{.Node.Hostname}}", node_hostname).replace(
140+
"{{.Task.Slot}}", str(task_slot)
141+
)
142+
143+
124144
@pytest.fixture
125145
def docker_compose_service_environment_dict(
126-
services_docker_compose_dict: dict[str, Any],
127-
env_devel_dict: EnvVarsDict,
146+
docker_compose_services_dict: dict[str, Any],
128147
service_name: str,
148+
env_devel_dict: EnvVarsDict,
129149
env_devel_file: Path,
130150
) -> EnvVarsDict:
131151
"""Returns env vars dict from the docker-compose `environment` section
132152
133153
- env_devel_dict in environment_configs plugin
134154
- service_name needs to be defined
135155
"""
136-
service = services_docker_compose_dict["services"][service_name]
156+
service = docker_compose_services_dict["services"][service_name]
137157

138158
def _substitute(key, value) -> tuple[str, str]:
139-
if m := re.match(r"\${([^{}:-]\w+)", value):
159+
if m := re.match(r"\${([^{}:-]\w+)", f"{value}"):
140160
expected_env_var = m.group(1)
141161
try:
142162
# NOTE: if this raises, then the RHS env-vars in the docker-compose are

packages/pytest-simcore/src/pytest_simcore/repository_paths.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ def services_docker_compose_file(services_dir: Path) -> Path:
8585
return dcpath
8686

8787

88+
@pytest.fixture(scope="session")
89+
def services_docker_compose_dev_vendors_file(osparc_simcore_services_dir: Path) -> Path:
90+
"""Path to osparc-simcore/services/docker-compose-dev-vendors.yml file"""
91+
dcpath = osparc_simcore_services_dir / "docker-compose-dev-vendors.yml"
92+
assert dcpath.exists()
93+
return dcpath
94+
95+
8896
@pytest.fixture(scope="session")
8997
def pylintrc(osparc_simcore_root_dir: Path) -> Path:
9098
pylintrc = osparc_simcore_root_dir / ".pylintrc"

services/docker-compose-deploy.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@ services:
1717
image: ${DOCKER_REGISTRY:-itisfoundation}/director:${DOCKER_IMAGE_TAG:-latest}
1818
director-v2:
1919
image: ${DOCKER_REGISTRY:-itisfoundation}/director-v2:${DOCKER_IMAGE_TAG:-latest}
20+
docker-api-proxy:
21+
image: ${DOCKER_REGISTRY:-itisfoundation}/docker-api-proxy:${DOCKER_IMAGE_TAG:-latest}
2022
dynamic-sidecar:
2123
image: ${DOCKER_REGISTRY:-itisfoundation}/dynamic-sidecar:${DOCKER_IMAGE_TAG:-latest}
24+
dynamic-scheduler:
25+
image: ${DOCKER_REGISTRY:-itisfoundation}/dynamic-scheduler:${DOCKER_IMAGE_TAG:-latest}
2226
efs-guardian:
2327
image: ${DOCKER_REGISTRY:-itisfoundation}/efs-guardian:${DOCKER_IMAGE_TAG:-latest}
2428
invitations:
@@ -29,10 +33,6 @@ services:
2933
image: ${DOCKER_REGISTRY:-itisfoundation}/notifications:${DOCKER_IMAGE_TAG:-latest}
3034
payments:
3135
image: ${DOCKER_REGISTRY:-itisfoundation}/payments:${DOCKER_IMAGE_TAG:-latest}
32-
dynamic-scheduler:
33-
image: ${DOCKER_REGISTRY:-itisfoundation}/dynamic-scheduler:${DOCKER_IMAGE_TAG:-latest}
34-
docker-api-proxy:
35-
image: ${DOCKER_REGISTRY:-itisfoundation}/docker-api-proxy:${DOCKER_IMAGE_TAG:-latest}
3636
resource-usage-tracker:
3737
image: ${DOCKER_REGISTRY:-itisfoundation}/resource-usage-tracker:${DOCKER_IMAGE_TAG:-latest}
3838
service-integration:

services/docker-compose-dev-vendors.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ services:
1515
- traefik.enable=true
1616
- traefik.swarm.network=${SWARM_STACK_NAME}_default
1717
# auth: https://doc.traefik.io/traefik/middlewares/http/forwardauth
18-
- traefik.http.middlewares.${SWARM_STACK_NAME}_manual-auth.forwardauth.address=http://${WEBSERVER_HOST}:${WEBSERVER_PORT}/v0/auth:check
18+
- traefik.http.middlewares.${SWARM_STACK_NAME}_manual-auth.forwardauth.address=http://${WB_AUTH_WEBSERVER_HOST}:${WB_AUTH_WEBSERVER_PORT}/v0/auth:check
1919
- traefik.http.middlewares.${SWARM_STACK_NAME}_manual-auth.forwardauth.trustForwardHeader=true
2020
- traefik.http.middlewares.${SWARM_STACK_NAME}_manual-auth.forwardauth.authResponseHeaders=Set-Cookie,osparc-sc2
2121
# routing

services/docker-compose.devel.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,13 @@ services:
150150
WEBSERVER_LOGLEVEL: DEBUG
151151
WEBSERVER_PROFILING: ${WEBSERVER_PROFILING}
152152
WEBSERVER_REMOTE_DEBUGGING_PORT: 3000
153-
WEBSERVER_FUNCTIONS: ${WEBSERVER_FUNCTIONS}
154153

155154

155+
wb-auth:
156+
volumes: *webserver_volumes_devel
157+
environment:
158+
<<: *webserver_environment_devel
159+
156160
wb-api-server:
157161
volumes: *webserver_volumes_devel
158162
environment:

services/docker-compose.local.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,13 @@ services:
166166
- traefik.http.routers.${SWARM_STACK_NAME}_webserver_local.priority=9
167167
- traefik.http.routers.${SWARM_STACK_NAME}_webserver_local.middlewares=${SWARM_STACK_NAME}_gzip@swarm, ${SWARM_STACK_NAME_NO_HYPHEN}_sslheader@swarm, ${SWARM_STACK_NAME}_webserver_retry
168168

169+
wb-auth:
170+
environment:
171+
<<: *webserver_environment_local
172+
ports:
173+
- "8080"
174+
- "3024:3000"
175+
169176
wb-api-server:
170177
environment:
171178
<<: *webserver_environment_local

services/docker-compose.yml

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -964,7 +964,7 @@ services:
964964
WEBSERVER_GARBAGE_COLLECTOR: ${WB_DB_EL_GARBAGE_COLLECTOR}
965965
WEBSERVER_GROUPS: ${WB_DB_EL_GROUPS}
966966
WEBSERVER_INVITATIONS: ${WB_DB_EL_INVITATIONS}
967-
WEBSERVER_LICENSES: null
967+
WEBSERVER_LICENSES: "null"
968968
WEBSERVER_LOGIN: ${WB_DB_EL_LOGIN}
969969
WEBSERVER_PAYMENTS: ${WB_DB_EL_PAYMENTS}
970970
WEBSERVER_NOTIFICATIONS: ${WB_DB_EL_NOTIFICATIONS}
@@ -1077,7 +1077,7 @@ services:
10771077
WEBSERVER_GROUPS: ${WB_GC_GROUPS}
10781078
WEBSERVER_HOST: ${WEBSERVER_HOST}
10791079
WEBSERVER_INVITATIONS: ${WB_GC_INVITATIONS}
1080-
WEBSERVER_LICENSES: null
1080+
WEBSERVER_LICENSES: "null"
10811081
WEBSERVER_LOGIN: ${WB_GC_LOGIN}
10821082
WEBSERVER_LOGLEVEL: ${WB_GC_LOGLEVEL}
10831083
WEBSERVER_NOTIFICATIONS: ${WB_GC_NOTIFICATIONS}
@@ -1099,6 +1099,70 @@ services:
10991099
- default
11001100
- interactive_services_subnet
11011101

1102+
wb-auth:
1103+
image: ${DOCKER_REGISTRY:-itisfoundation}/webserver:${DOCKER_IMAGE_TAG:-latest}
1104+
init: true
1105+
hostname: "auth-{{.Node.Hostname}}-{{.Task.Slot}}" # the hostname is used in conjonction with other services and must be unique see https://github.com/ITISFoundation/osparc-simcore/pull/5931
1106+
environment:
1107+
WEBSERVER_APP_FACTORY_NAME: WEBSERVER_AUTHZ_APP_FACTORY
1108+
WEBSERVER_LOGLEVEL: ${WB_AUTH_LOGLEVEL}
1109+
GUNICORN_CMD_ARGS: ${WEBSERVER_GUNICORN_CMD_ARGS}
1110+
1111+
# WEBSERVER_DB
1112+
POSTGRES_DB: ${POSTGRES_DB}
1113+
POSTGRES_ENDPOINT: ${POSTGRES_ENDPOINT}
1114+
POSTGRES_HOST: ${POSTGRES_HOST}
1115+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
1116+
POSTGRES_PORT: ${POSTGRES_PORT}
1117+
POSTGRES_USER: ${POSTGRES_USER}
1118+
1119+
# WEBSERVER_REST
1120+
REST_SWAGGER_API_DOC_ENABLED: 0
1121+
1122+
# WEBSERVER_SERVER_HOST
1123+
WEBSERVER_HOST: ${WB_AUTH_WEBSERVER_HOST}
1124+
WEBSERVER_PORT: ${WB_AUTH_WEBSERVER_PORT}
1125+
1126+
# WEBSERVER_SESSION Enabled
1127+
SESSION_SECRET_KEY: ${WEBSERVER_SESSION_SECRET_KEY}
1128+
SESSION_COOKIE_MAX_AGE: ${SESSION_COOKIE_MAX_AGE}
1129+
SESSION_COOKIE_SAMESITE: ${SESSION_COOKIE_SAMESITE}
1130+
SESSION_COOKIE_SECURE: ${SESSION_COOKIE_SECURE}
1131+
SESSION_COOKIE_HTTPONLY: ${SESSION_COOKIE_HTTPONLY}
1132+
1133+
1134+
WEBSERVER_ACTIVITY: "null"
1135+
WEBSERVER_ANNOUNCEMENTS: 0
1136+
WEBSERVER_CATALOG: "null"
1137+
WEBSERVER_DB_LISTENER: 0
1138+
WEBSERVER_DIRECTOR_V2: "null"
1139+
WEBSERVER_EMAIL: "null"
1140+
WEBSERVER_EXPORTER: "null"
1141+
WEBSERVER_FOLDERS: 0
1142+
WEBSERVER_FRONTEND: "null"
1143+
WEBSERVER_FUNCTIONS: 0
1144+
WEBSERVER_GARBAGE_COLLECTOR: "null"
1145+
WEBSERVER_GROUPS: 0
1146+
WEBSERVER_INVITATIONS: "null"
1147+
WEBSERVER_LICENSES: "null"
1148+
WEBSERVER_LOGIN: "null"
1149+
WEBSERVER_NOTIFICATIONS: 0
1150+
WEBSERVER_PAYMENTS: "null"
1151+
WEBSERVER_PRODUCTS: 1
1152+
WEBSERVER_PROJECTS: "null"
1153+
WEBSERVER_PUBLICATIONS: 0
1154+
WEBSERVER_RABBITMQ: "null"
1155+
WEBSERVER_REALTIME_COLLABORATION: "null"
1156+
WEBSERVER_REDIS: "null" # TODO: cache?
1157+
WEBSERVER_RESOURCE_USAGE_TRACKER: "null"
1158+
WEBSERVER_SCICRUNCH: "null"
1159+
WEBSERVER_SOCKETIO: 0
1160+
WEBSERVER_STATICWEB: "null"
1161+
WEBSERVER_STORAGE: "null"
1162+
WEBSERVER_STUDIES_DISPATCHER: "null"
1163+
WEBSERVER_TAGS: 0
1164+
WEBSERVER_USERS: "null"
1165+
11021166
agent:
11031167
image: ${DOCKER_REGISTRY:-itisfoundation}/agent:${DOCKER_IMAGE_TAG:-latest}
11041168
init: true

services/web/server/src/simcore_service_webserver/_meta.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
api_version_prefix: str = API_VTAG
2424

2525

26-
# kids drawings :-)
27-
2826
WELCOME_MSG = r"""
2927
_ _ _
3028
| | | | | |
@@ -45,6 +43,7 @@
4543
(_)) __((/ __|
4644
| (_ || (__
4745
\___| \___|
46+
4847
"""
4948

5049
WELCOME_DB_LISTENER_MSG = r"""
@@ -54,5 +53,17 @@
5453
| | | _ <___| |--| |___ \- -| __| | | __| _ <
5554
|_____\_____/ \_____\___<_____/|__|\_____\__|__\_____\__|\_/
5655
57-
5856
"""
57+
58+
# SEE https://patorjk.com/software/taag/#p=display&f=BlurVision%20ASCII&t=Auth%0A
59+
WELCOME_AUTH_APP_MSG = r"""
60+
░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░
61+
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░
62+
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░
63+
░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓████████▓▒░
64+
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░
65+
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░
66+
░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ {}
67+
""".format(
68+
f"v{__version__}"
69+
)

services/web/server/src/simcore_service_webserver/application.py

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"""Main application"""
33

44
import logging
5+
from collections.abc import Callable
56
from pprint import pformat
67
from typing import Any
78

@@ -11,7 +12,13 @@
1112
setup_realtime_collaboration,
1213
)
1314

14-
from ._meta import WELCOME_DB_LISTENER_MSG, WELCOME_GC_MSG, WELCOME_MSG, info
15+
from ._meta import (
16+
WELCOME_AUTH_APP_MSG,
17+
WELCOME_DB_LISTENER_MSG,
18+
WELCOME_GC_MSG,
19+
WELCOME_MSG,
20+
info,
21+
)
1522
from .activity.plugin import setup_activity
1623
from .announcements.plugin import setup_announcements
1724
from .api_keys.plugin import setup_api_keys
@@ -62,18 +69,29 @@
6269
_logger = logging.getLogger(__name__)
6370

6471

65-
async def _welcome_banner(app: web.Application):
66-
settings = get_application_settings(app)
67-
print(WELCOME_MSG, flush=True) # noqa: T201
68-
if settings.WEBSERVER_GARBAGE_COLLECTOR:
69-
print("with", WELCOME_GC_MSG, flush=True) # noqa: T201
70-
if settings.WEBSERVER_DB_LISTENER:
71-
print("with", WELCOME_DB_LISTENER_MSG, flush=True) # noqa: T201
72+
def _create_welcome_banner(banner_msg: str) -> Callable:
73+
"""Creates a welcome banner function with optional GC and DB listener messages"""
74+
75+
async def _welcome_banner(app: web.Application):
76+
settings = get_application_settings(app)
77+
78+
print(banner_msg, flush=True) # noqa: T201
79+
if settings.WEBSERVER_GARBAGE_COLLECTOR:
80+
print("with", WELCOME_GC_MSG, flush=True) # noqa: T201
81+
if settings.WEBSERVER_DB_LISTENER:
82+
print("with", WELCOME_DB_LISTENER_MSG, flush=True) # noqa: T201
83+
84+
return _welcome_banner
85+
86+
87+
def _create_finished_banner() -> Callable:
88+
"""Creates a finished banner function"""
7289

90+
async def _finished_banner(app: web.Application):
91+
assert app # nosec
92+
print(info.get_finished_banner(), flush=True) # noqa: T201
7393

74-
async def _finished_banner(app: web.Application):
75-
assert app # nosec
76-
print(info.get_finished_banner(), flush=True) # noqa: T201
94+
return _finished_banner
7795

7896

7997
def create_application() -> web.Application:
@@ -166,8 +184,8 @@ def create_application() -> web.Application:
166184
setup_realtime_collaboration(app)
167185

168186
# NOTE: *last* events
169-
app.on_startup.append(_welcome_banner)
170-
app.on_shutdown.append(_finished_banner)
187+
app.on_startup.append(_create_welcome_banner(WELCOME_MSG))
188+
app.on_shutdown.append(_create_finished_banner())
171189

172190
_logger.debug("Routes in app: \n %s", pformat(app.router.named_resources()))
173191

@@ -183,8 +201,8 @@ def create_application_auth() -> web.Application:
183201
setup_login_auth(app)
184202

185203
# NOTE: *last* events
186-
app.on_startup.append(_welcome_banner)
187-
app.on_shutdown.append(_finished_banner)
204+
app.on_startup.append(_create_welcome_banner(WELCOME_AUTH_APP_MSG))
205+
app.on_shutdown.append(_create_finished_banner())
188206

189207
_logger.debug(
190208
"Routes in application-auth: \n %s", pformat(app.router.named_resources())

0 commit comments

Comments
 (0)