Skip to content

Commit 33d0175

Browse files
authored
Merge branch 'master' into feature/connect-jobs
2 parents 4cbd2d0 + 5e1323e commit 33d0175

File tree

66 files changed

+632
-448
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+632
-448
lines changed

api/specs/web-server/_auth_api_keys.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
from models_library.generics import Envelope
1010
from models_library.rest_error import EnvelopedError
1111
from simcore_service_webserver._meta import API_VTAG
12-
from simcore_service_webserver.api_keys._controller_rest import ApiKeysPathParams
13-
from simcore_service_webserver.api_keys._controller_rest_exceptions import (
12+
from simcore_service_webserver.api_keys._controller.rest import ApiKeysPathParams
13+
from simcore_service_webserver.api_keys._controller.rest_exceptions import (
1414
_TO_HTTP_ERROR_MAP,
1515
)
1616

packages/models-library/src/models_library/rpc/webserver/auth/api_keys.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,32 @@
11
import datetime as dt
2-
from typing import Annotated
2+
import hashlib
3+
import re
4+
import secrets
5+
import string
6+
from typing import Annotated, Final
37

48
from models_library.basic_types import IDStr
59
from pydantic import BaseModel, ConfigDict, Field
610

11+
_PUNCTUATION_REGEX = re.compile(
12+
pattern="[" + re.escape(string.punctuation.replace("_", "")) + "]"
13+
)
14+
15+
_KEY_LEN: Final = 10
16+
_SECRET_LEN: Final = 20
17+
18+
19+
def generate_unique_api_key(name: str, length: int = _KEY_LEN) -> str:
20+
prefix = _PUNCTUATION_REGEX.sub("_", name[:5])
21+
hashed = hashlib.sha256(name.encode()).hexdigest()
22+
return f"{prefix}_{hashed[:length]}"
23+
24+
25+
def generate_api_key_and_secret(name: str):
26+
api_key = generate_unique_api_key(name)
27+
api_secret = secrets.token_hex(_SECRET_LEN)
28+
return api_key, api_secret
29+
730

831
class ApiKeyCreate(BaseModel):
932
display_name: Annotated[str, Field(..., min_length=3)]
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""hash existing api_secret data
2+
3+
Revision ID: 742123f0933a
4+
Revises: b0c988e3f348
5+
Create Date: 2025-03-13 09:39:43.895529+00:00
6+
7+
"""
8+
9+
from alembic import op
10+
11+
# revision identifiers, used by Alembic.
12+
revision = "742123f0933a"
13+
down_revision = "b0c988e3f348"
14+
branch_labels = None
15+
depends_on = None
16+
17+
18+
def upgrade():
19+
op.execute(
20+
"""
21+
UPDATE api_keys
22+
SET api_secret = crypt(api_secret, gen_salt('bf', 10))
23+
"""
24+
)
25+
26+
27+
def downgrade():
28+
pass
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""add index to api_key column
2+
3+
Revision ID: b0c988e3f348
4+
Revises: f65f7786cd4b
5+
Create Date: 2025-03-13 08:53:05.722855+00:00
6+
7+
"""
8+
9+
from alembic import op
10+
11+
# revision identifiers, used by Alembic.
12+
revision = "b0c988e3f348"
13+
down_revision = "f65f7786cd4b"
14+
branch_labels = None
15+
depends_on = None
16+
17+
18+
def upgrade():
19+
op.create_index(op.f("ix_api_keys_api_key"), "api_keys", ["api_key"], unique=False)
20+
21+
22+
def downgrade():
23+
op.drop_index(op.f("ix_api_keys_api_key"), table_name="api_keys")

packages/postgres-database/src/simcore_postgres_database/models/api_keys.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
""" API keys to access public API
1+
"""API keys to access public API
22
33
44
These keys grant the client authorization to the API resources
@@ -10,6 +10,7 @@
1010
+--------+ +---------------+
1111
1212
"""
13+
1314
import sqlalchemy as sa
1415
from sqlalchemy.sql import func
1516

@@ -52,8 +53,13 @@
5253
nullable=False,
5354
doc="Identified product",
5455
),
55-
sa.Column("api_key", sa.String(), nullable=False),
56-
sa.Column("api_secret", sa.String(), nullable=False),
56+
sa.Column("api_key", sa.String(), nullable=False, index=True),
57+
sa.Column(
58+
"api_secret",
59+
sa.String(),
60+
nullable=False,
61+
doc="API key secret, hashed using blowfish algorithm",
62+
),
5763
sa.Column(
5864
"created",
5965
sa.DateTime(),

packages/postgres-database/tests/test_models_api_keys.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from aiopg.sa.connection import SAConnection
1111
from aiopg.sa.result import RowProxy
1212
from pytest_simcore.helpers.faker_factories import (
13-
random_api_key,
13+
random_api_auth,
1414
random_product,
1515
random_user,
1616
)
@@ -48,7 +48,7 @@ async def test_create_and_delete_api_key(
4848
):
4949
apikey_id = await connection.scalar(
5050
api_keys.insert()
51-
.values(**random_api_key(product_name, user_id))
51+
.values(**random_api_auth(product_name, user_id))
5252
.returning(api_keys.c.id)
5353
)
5454

@@ -66,7 +66,7 @@ async def session_auth(
6666
# uses to authenticate a session.
6767
result = await connection.execute(
6868
api_keys.insert()
69-
.values(**random_api_key(product_name, user_id))
69+
.values(**random_api_auth(product_name, user_id))
7070
.returning(sa.literal_column("*"))
7171
)
7272
row = await result.fetchone()

packages/pytest-simcore/src/pytest_simcore/helpers/faker_factories.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ def random_payment_autorecharge(
405405
return data
406406

407407

408-
def random_api_key(
408+
def random_api_auth(
409409
product_name: str, user_id: int, fake: Faker = DEFAULT_FAKER, **overrides
410410
) -> dict[str, Any]:
411411
from simcore_postgres_database.models.api_keys import api_keys

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/webserver/auth/api_keys.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from models_library.basic_types import IDStr
55
from models_library.rabbitmq_basic_types import RPCMethodName
66
from models_library.rpc.webserver.auth.api_keys import ApiKeyCreate, ApiKeyGet
7+
from models_library.users import UserID
78
from pydantic import TypeAdapter
89
from servicelib.logging_utils import log_decorator
910
from servicelib.rabbitmq import RabbitMQRPCClient
@@ -15,7 +16,7 @@
1516
async def create_api_key(
1617
rabbitmq_rpc_client: RabbitMQRPCClient,
1718
*,
18-
user_id: str,
19+
user_id: UserID,
1920
product_name: str,
2021
api_key: ApiKeyCreate,
2122
) -> ApiKeyGet:
@@ -24,7 +25,8 @@ async def create_api_key(
2425
TypeAdapter(RPCMethodName).validate_python("create_api_key"),
2526
user_id=user_id,
2627
product_name=product_name,
27-
api_key=api_key,
28+
display_name=api_key.display_name,
29+
expiration=api_key.expiration,
2830
)
2931
assert isinstance(result, ApiKeyGet) # nosec
3032
return result
@@ -34,7 +36,7 @@ async def create_api_key(
3436
async def get_api_key(
3537
rabbitmq_rpc_client: RabbitMQRPCClient,
3638
*,
37-
user_id: str,
39+
user_id: UserID,
3840
product_name: str,
3941
api_key_id: IDStr,
4042
) -> ApiKeyGet:
@@ -49,18 +51,19 @@ async def get_api_key(
4951
return result
5052

5153

52-
async def delete_api_key(
54+
@log_decorator(_logger, level=logging.DEBUG)
55+
async def delete_api_key_by_key(
5356
rabbitmq_rpc_client: RabbitMQRPCClient,
5457
*,
55-
user_id: str,
58+
user_id: UserID,
5659
product_name: str,
57-
api_key_id: IDStr,
60+
api_key: str,
5861
) -> None:
5962
result = await rabbitmq_rpc_client.request(
6063
WEBSERVER_RPC_NAMESPACE,
61-
TypeAdapter(RPCMethodName).validate_python("delete_api_key"),
64+
TypeAdapter(RPCMethodName).validate_python("delete_api_key_by_key"),
6265
user_id=user_id,
6366
product_name=product_name,
64-
api_key_id=api_key_id,
67+
api_key=api_key,
6568
)
6669
assert result is None # nosec

scripts/docker/healthcheck_curl_host.py

100644100755
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
COPY --chown=scu:scu docker/healthcheck.py docker/healthcheck.py
77
HEALTHCHECK --interval=30s \
88
--timeout=30s \
9-
--start-period=1s \
9+
--start-period=20s \
10+
--start-interval=1s \
1011
--retries=3 \
1112
CMD python3 docker/healthcheck.py http://localhost:8080/v0/
1213
```

services/agent/Dockerfile

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,13 @@ COPY --chown=scu:scu services/agent/docker services/agent/docker
148148
RUN chmod +x services/agent/docker/*.sh
149149

150150

151-
HEALTHCHECK --interval=30s \
152-
--timeout=20s \
153-
--start-period=30s \
154-
--retries=3 \
151+
# https://docs.docker.com/reference/dockerfile/#healthcheck
152+
HEALTHCHECK \
153+
--interval=10s \
154+
--timeout=5s \
155+
--start-period=20s \
156+
--start-interval=1s \
157+
--retries=5 \
155158
CMD ["python3", "services/agent/docker/healthcheck.py", "http://localhost:8000/health"]
156159

157160
EXPOSE 8000

0 commit comments

Comments
 (0)