diff --git a/.bumpversion.cfg b/.bumpversion.cfg index af59d8454..16e1fca22 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 4.1.0-rc4 +current_version = 4.1.0 tag_name = {new_version} commit = True tag = True diff --git a/aleph/logic/api_keys.py b/aleph/logic/api_keys.py index 96f960f3f..a8b1c0576 100644 --- a/aleph/logic/api_keys.py +++ b/aleph/logic/api_keys.py @@ -129,7 +129,6 @@ def hash_plaintext_api_keys(): for index, partition in enumerate(results.partitions()): for role in partition: role.api_key_digest = hash_api_key(role.api_key) - role.api_key = None db.session.add(role) log.info(f"Hashing API key: {role}") log.info(f"Comitting partition {index}") diff --git a/aleph/model/role.py b/aleph/model/role.py index 7d3cc8f74..fcdaa67a1 100644 --- a/aleph/model/role.py +++ b/aleph/model/role.py @@ -1,7 +1,7 @@ import logging from datetime import datetime, timezone from normality import stringify -from sqlalchemy import or_, not_, func +from sqlalchemy import and_, or_, not_, func from itsdangerous import URLSafeTimedSerializer from werkzeug.security import generate_password_hash, check_password_hash @@ -197,13 +197,18 @@ def by_email(cls, email): @classmethod def by_api_key(cls, api_key): - if api_key is None: + if api_key is None or not len(api_key.strip()): return None q = cls.all() digest = hash_api_key(api_key) - q = q.filter(cls.api_key_digest == digest) + q = q.filter( + and_( + cls.api_key_digest != None, # noqa: E711 + cls.api_key_digest == digest, + ) + ) utcnow = datetime.now(timezone.utc) # TODO: Exclude API keys without expiration date after deadline diff --git a/aleph/tests/test_api_keys.py b/aleph/tests/test_api_keys.py index 111b7f791..1f155c930 100644 --- a/aleph/tests/test_api_keys.py +++ b/aleph/tests/test_api_keys.py @@ -211,7 +211,12 @@ def test_hash_plaintext_api_keys(self): hash_plaintext_api_keys() db.session.refresh(user_1) - assert user_1.api_key is None + + # Do not delete the plaintext API key to allow for version rollbacks. + # `api_key` column will be removed in the next version at which point all + # plaintext keys will be deleted. + assert user_1.api_key == "1234567890" + assert user_1.api_key_digest == hash_api_key("1234567890") db.session.refresh(user_2) diff --git a/aleph/tests/test_view_context.py b/aleph/tests/test_view_context.py index 2edf9cdf0..fbdee72b5 100644 --- a/aleph/tests/test_view_context.py +++ b/aleph/tests/test_view_context.py @@ -75,6 +75,10 @@ def test_authz_header_api_key_invalid(self): res = self.client.get(f"/api/2/roles/{self.role.id}", headers=headers) assert res.status_code == 403 + headers = {"Authorization": "ApiKey "} + res = self.client.get(f"/api/2/roles/{self.role.id}", headers=headers) + assert res.status_code == 403 + headers = {"Authorization": ""} res = self.client.get(f"/api/2/roles/{self.role.id}", headers=headers) assert res.status_code == 403 @@ -83,6 +87,10 @@ def test_authz_header_api_key_invalid(self): res = self.client.get(f"/api/2/roles/{self.role.id}", headers=headers) assert res.status_code == 403 + headers = {"Authorization": " "} + res = self.client.get(f"/api/2/roles/{self.role.id}", headers=headers) + assert res.status_code == 403 + def test_authz_url_param_api_key(self): query_string = {"api_key": "1234567890"} res = self.client.get(f"/api/2/roles/{self.role.id}", query_string=query_string) @@ -97,3 +105,7 @@ def test_authz_url_params_api_key_invalid(self): query_string = {"api_key": ""} res = self.client.get(f"/api/2/roles/{self.role.id}", query_string=query_string) assert res.status_code == 403 + + query_string = {"api_key": " "} + res = self.client.get(f"/api/2/roles/{self.role.id}", query_string=query_string) + assert res.status_code == 403 diff --git a/aleph/worker.py b/aleph/worker.py index da7b05157..6f4942b70 100644 --- a/aleph/worker.py +++ b/aleph/worker.py @@ -3,7 +3,6 @@ import time import threading import functools -import queue import copy from typing import Dict, Callable import sqlalchemy @@ -116,7 +115,13 @@ def __init__( version=None, prefetch_count_mapping=defaultdict(lambda: 1), ): - super().__init__(queues, conn=conn, num_threads=num_threads, version=version) + super().__init__( + queues, + conn=conn, + num_threads=num_threads, + version=version, + prefetch_count_mapping=prefetch_count_mapping, + ) self.often = get_rate_limit("often", unit=300, interval=1, limit=1) self.daily = get_rate_limit("daily", unit=3600, interval=24, limit=1) # special treatment for indexing jobs - indexing jobs need to be batched @@ -125,7 +130,6 @@ def __init__( # run of all available indexing tasks self.indexing_batch_last_updated = 0.0 self.indexing_batches = defaultdict(list) - self.local_queue = queue.Queue() self.prefetch_count_mapping = prefetch_count_mapping def on_signal(self, signal, _): diff --git a/aleph/wsgi.py b/aleph/wsgi.py index 96e9cea9c..fbcf869ac 100644 --- a/aleph/wsgi.py +++ b/aleph/wsgi.py @@ -1,3 +1,5 @@ +import logging + from aleph.core import create_app from aleph.settings import SETTINGS from aleph import __version__ as aleph_version @@ -5,6 +7,7 @@ import sentry_sdk from sentry_sdk.integrations.flask import FlaskIntegration +log = logging.getLogger(__name__) if SETTINGS.SENTRY_DSN: sentry_sdk.init( @@ -17,4 +20,5 @@ environment=SETTINGS.SENTRY_ENVIRONMENT, send_default_pii=False, ) +log.info("aleph.wsgi initialized Sentry SDK") app = create_app() diff --git a/contrib/aleph-traefik-minio-keycloak/docker-compose.yml b/contrib/aleph-traefik-minio-keycloak/docker-compose.yml index 1ddb16302..2c9be830c 100644 --- a/contrib/aleph-traefik-minio-keycloak/docker-compose.yml +++ b/contrib/aleph-traefik-minio-keycloak/docker-compose.yml @@ -54,7 +54,7 @@ services: - "traefik.enable=false" worker: - image: ghcr.io/alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0-rc4} + image: ghcr.io/alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0} command: aleph worker restart: on-failure links: @@ -79,7 +79,7 @@ services: - "traefik.enable=false" shell: - image: ghcr.io/alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0-rc4} + image: ghcr.io/alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0} command: /bin/bash depends_on: - postgres @@ -99,7 +99,7 @@ services: - "traefik.enable=false" api: - image: ghcr.io/alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0-rc4} + image: ghcr.io/alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0} command: gunicorn -w 6 -b 0.0.0.0:8000 --log-level debug --log-file - aleph.wsgi:app expose: - 8000 @@ -121,7 +121,7 @@ services: - "traefik.enable=false" ui: - image: ghcr.io/alephdata/aleph-ui-production:${ALEPH_TAG:-ALEPH_TAG:-4.1.0-rc4} + image: ghcr.io/alephdata/aleph-ui-production:${ALEPH_TAG:-ALEPH_TAG:-4.1.0} depends_on: - api - traefik diff --git a/contrib/keycloak/docker-compose.dev-keycloak.yml b/contrib/keycloak/docker-compose.dev-keycloak.yml index 23ef4605f..b7a48e692 100644 --- a/contrib/keycloak/docker-compose.dev-keycloak.yml +++ b/contrib/keycloak/docker-compose.dev-keycloak.yml @@ -16,7 +16,7 @@ services: elasticsearch: build: context: services/elasticsearch - image: ghcr.io/alephdata/aleph-elasticsearch:${ALEPH_TAG:-ALEPH_TAG:-4.1.0-rc4} + image: ghcr.io/alephdata/aleph-elasticsearch:${ALEPH_TAG:-ALEPH_TAG:-4.1.0} hostname: elasticsearch environment: - discovery.type=single-node @@ -55,7 +55,7 @@ services: app: build: context: . - image: alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0-rc4} + image: alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0} hostname: aleph command: /bin/bash links: @@ -83,7 +83,7 @@ services: api: build: context: . - image: alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0-rc4} + image: alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0} command: aleph run -h 0.0.0.0 -p 5000 --with-threads --reload --debugger ports: - "127.0.0.1:5000:5000" @@ -117,7 +117,7 @@ services: ui: build: context: ui - image: alephdata/aleph-ui:${ALEPH_TAG:-ALEPH_TAG:-4.1.0-rc4} + image: alephdata/aleph-ui:${ALEPH_TAG:-ALEPH_TAG:-4.1.0} links: - api command: npm run start diff --git a/docker-compose.yml b/docker-compose.yml index 810cab902..5cf69f746 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -46,7 +46,7 @@ services: - aleph.env worker: - image: ghcr.io/alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0-rc4} + image: ghcr.io/alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0} command: aleph worker restart: on-failure depends_on: @@ -62,7 +62,7 @@ services: - aleph.env shell: - image: ghcr.io/alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0-rc4} + image: ghcr.io/alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0} command: /bin/bash depends_on: - postgres @@ -80,7 +80,7 @@ services: - aleph.env api: - image: ghcr.io/alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0-rc4} + image: ghcr.io/alephdata/aleph:${ALEPH_TAG:-ALEPH_TAG:-4.1.0} expose: - 8000 depends_on: @@ -97,7 +97,7 @@ services: - aleph.env ui: - image: ghcr.io/alephdata/aleph-ui-production:${ALEPH_TAG:-ALEPH_TAG:-4.1.0-rc4} + image: ghcr.io/alephdata/aleph-ui-production:${ALEPH_TAG:-ALEPH_TAG:-4.1.0} depends_on: - api ports: diff --git a/helm/charts/aleph/Chart.yaml b/helm/charts/aleph/Chart.yaml index df5a04952..c45047028 100644 --- a/helm/charts/aleph/Chart.yaml +++ b/helm/charts/aleph/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: aleph description: Helm chart for Aleph type: application -version: 4.1.0-rc4 -appVersion: 4.1.0-rc4 \ No newline at end of file +version: 4.1.0 +appVersion: 4.1.0 \ No newline at end of file diff --git a/helm/charts/aleph/README.md b/helm/charts/aleph/README.md index 6a1ad52a5..7f929071c 100644 --- a/helm/charts/aleph/README.md +++ b/helm/charts/aleph/README.md @@ -11,7 +11,7 @@ Helm chart for Aleph | global.amazon | bool | `true` | Are we using AWS services like s3? | | global.google | bool | `false` | Are we using GCE services like storage, vision api? | | global.image.repository | string | `"alephdata/aleph"` | Aleph docker image repo | -| global.image.tag | string | `"global.image.tag | string | `"4.1.0-rc4"` | Aleph docker image tag | +| global.image.tag | string | `"global.image.tag | string | `"4.1.0"` | Aleph docker image tag | | global.image.tag | string | `"Always"` | | | global.namingPrefix | string | `"aleph"` | Prefix for the names of k8s resources | diff --git a/helm/charts/aleph/values.yaml b/helm/charts/aleph/values.yaml index 6307f5242..0aa54a080 100644 --- a/helm/charts/aleph/values.yaml +++ b/helm/charts/aleph/values.yaml @@ -6,7 +6,7 @@ global: image: repository: ghcr.io/alephdata/aleph - tag: "4.1.0-rc4" + tag: "4.1.0" pullPolicy: Always commonEnv: diff --git a/requirements.txt b/requirements.txt index 8aa8c15c4..a0670e687 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ followthemoney==3.5.9 followthemoney-store[postgresql]==3.1.0 followthemoney-compare==0.4.4 fingerprints==1.2.3 -servicelayer[google,amazon]==1.23.3-rc7 +servicelayer[google,amazon]==1.24.0 normality==2.5.0 pantomime==0.6.1 diff --git a/setup.py b/setup.py index 4ae83513a..8c3941e29 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="aleph", - version="4.1.0-rc4", + version="4.1.0", description="Document sifting web frontend", classifiers=[ "Intended Audience :: Developers", diff --git a/ui/package.json b/ui/package.json index 44af3e415..1f8a00a59 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "aleph-ui", - "version": "4.1.0-rc4", + "version": "4.1.0", "private": true, "dependencies": { "@alephdata/followthemoney": "^3.5.5", diff --git a/ui/src/components/Entity/EntityViews.jsx b/ui/src/components/Entity/EntityViews.jsx index 1cf9b6027..3cc1270bf 100644 --- a/ui/src/components/Entity/EntityViews.jsx +++ b/ui/src/components/Entity/EntityViews.jsx @@ -174,8 +174,9 @@ class EntityViews extends React.Component { // The view mode is the default mode. It is rendered for almost all document-like // entities (except folders) and renders an empty state if no viewer is available - // for a specific file type. - if (entity.schema.isA('Folder')) { + // for a specific file type. Email is special cased, because it inherits from Folder, + // but still has a normal view. + if (entity.schema.isA('Folder') && !entity.schema.isA('Email')) { return; }