From e2e11931023e7b7e76d50c1d5b235bc4c437579a Mon Sep 17 00:00:00 2001 From: Sylvain Leclerc Date: Tue, 3 Sep 2024 16:27:19 +0200 Subject: [PATCH 1/4] Fix licensing-related issues - improve checker to skip licensed subfolders - remove some wrong copyrights from forked projects Signed-off-by: Sylvain Leclerc --- .../core/utils/fastapi_sqlalchemy/LICENSE | 21 ++++ .../core/utils/fastapi_sqlalchemy/README.md | 3 +- .../core/utils/fastapi_sqlalchemy/__init__.py | 12 -- .../utils/fastapi_sqlalchemy/exceptions.py | 13 -- .../utils/fastapi_sqlalchemy/middleware.py | 12 -- antarest/fastapi_jwt_auth/README.md | 4 + antarest/utils.py | 11 +- scripts/license_checker_and_adder.py | 117 +++++++++++------- 8 files changed, 108 insertions(+), 85 deletions(-) create mode 100644 antarest/core/utils/fastapi_sqlalchemy/LICENSE create mode 100644 antarest/fastapi_jwt_auth/README.md mode change 100644 => 100755 scripts/license_checker_and_adder.py diff --git a/antarest/core/utils/fastapi_sqlalchemy/LICENSE b/antarest/core/utils/fastapi_sqlalchemy/LICENSE new file mode 100644 index 0000000000..577fc21f6c --- /dev/null +++ b/antarest/core/utils/fastapi_sqlalchemy/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Michael Freeborn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/antarest/core/utils/fastapi_sqlalchemy/README.md b/antarest/core/utils/fastapi_sqlalchemy/README.md index 78c02a32a5..fae88f5abc 100644 --- a/antarest/core/utils/fastapi_sqlalchemy/README.md +++ b/antarest/core/utils/fastapi_sqlalchemy/README.md @@ -1,3 +1,4 @@ # FastAPI-SQLAlchemy -Forked from https://github.com/mfreeborn/fastapi-sqlalchemy \ No newline at end of file +Forked from https://github.com/mfreeborn/fastapi-sqlalchemy, +licensed under MIT license, see [LICENSE](LICENSE). diff --git a/antarest/core/utils/fastapi_sqlalchemy/__init__.py b/antarest/core/utils/fastapi_sqlalchemy/__init__.py index 66c4a96541..cdc6aaaaf5 100644 --- a/antarest/core/utils/fastapi_sqlalchemy/__init__.py +++ b/antarest/core/utils/fastapi_sqlalchemy/__init__.py @@ -1,15 +1,3 @@ -# Copyright (c) 2024, RTE (https://www.rte-france.com) -# -# See AUTHORS.txt -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# SPDX-License-Identifier: MPL-2.0 -# -# This file is part of the Antares project. - from antarest.core.utils.fastapi_sqlalchemy.middleware import DBSessionMiddleware, db __all__ = ["db", "DBSessionMiddleware"] diff --git a/antarest/core/utils/fastapi_sqlalchemy/exceptions.py b/antarest/core/utils/fastapi_sqlalchemy/exceptions.py index 56f06f4c22..ad1eccff2c 100644 --- a/antarest/core/utils/fastapi_sqlalchemy/exceptions.py +++ b/antarest/core/utils/fastapi_sqlalchemy/exceptions.py @@ -1,16 +1,3 @@ -# Copyright (c) 2024, RTE (https://www.rte-france.com) -# -# See AUTHORS.txt -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# SPDX-License-Identifier: MPL-2.0 -# -# This file is part of the Antares project. - - class MissingSessionError(Exception): """Exception raised for when the user tries to access a database session before it is created.""" diff --git a/antarest/core/utils/fastapi_sqlalchemy/middleware.py b/antarest/core/utils/fastapi_sqlalchemy/middleware.py index 89426d55a9..9a98b4ef1b 100644 --- a/antarest/core/utils/fastapi_sqlalchemy/middleware.py +++ b/antarest/core/utils/fastapi_sqlalchemy/middleware.py @@ -1,15 +1,3 @@ -# Copyright (c) 2024, RTE (https://www.rte-france.com) -# -# See AUTHORS.txt -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# SPDX-License-Identifier: MPL-2.0 -# -# This file is part of the Antares project. - from contextvars import ContextVar, Token from typing import Any, Dict, Optional, Type, Union diff --git a/antarest/fastapi_jwt_auth/README.md b/antarest/fastapi_jwt_auth/README.md new file mode 100644 index 0000000000..c4dfd9017e --- /dev/null +++ b/antarest/fastapi_jwt_auth/README.md @@ -0,0 +1,4 @@ +# FastAPI JWT Auth + +Forked from https://github.com/IndominusByte/fastapi-jwt-auth, +licensed under MIT license, see [LICENSE](LICENSE). diff --git a/antarest/utils.py b/antarest/utils.py index 60f520f2e7..c0ecffbcca 100644 --- a/antarest/utils.py +++ b/antarest/utils.py @@ -10,7 +10,6 @@ # # This file is part of the Antares project. -import datetime import logging from enum import Enum from pathlib import Path @@ -21,9 +20,17 @@ import uvicorn # type: ignore from fastapi import FastAPI from ratelimit import RateLimitMiddleware # type: ignore +from ratelimit import RateLimitMiddleware # type: ignore +from ratelimit.backends.redis import RedisBackend # type: ignore +from ratelimit.backends.redis import RedisBackend # type: ignore +from ratelimit.backends.redis import RedisBackend # type: ignore from ratelimit.backends.redis import RedisBackend # type: ignore from ratelimit.backends.simple import MemoryBackend # type: ignore -from sqlalchemy import create_engine +from ratelimit.backends.simple import MemoryBackend # type: ignore +from ratelimit.backends.simple import MemoryBackend # type: ignore +from ratelimit.backends.simple import MemoryBackend # type: ignore +from sqlalchemy import create_engine # type: ignore +from sqlalchemy import create_engine # type: ignore from sqlalchemy.engine.base import Engine # type: ignore from sqlalchemy.pool import NullPool # type: ignore diff --git a/scripts/license_checker_and_adder.py b/scripts/license_checker_and_adder.py old mode 100644 new mode 100755 index e5ac2828ec..2eb388a20f --- a/scripts/license_checker_and_adder.py +++ b/scripts/license_checker_and_adder.py @@ -1,16 +1,82 @@ -import glob import os +import re from pathlib import Path +from typing import List import click +# Use to skip subtrees that have their own licenses (forks) +LICENSE_FILE_PATTERN = re.compile("LICENSE.*") + +ANTARES_LICENSE_HEADER = """# Copyright (c) 2024, RTE (https://www.rte-france.com) +# +# See AUTHORS.txt +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# SPDX-License-Identifier: MPL-2.0 +# +# This file is part of the Antares project. +""" + +LICENSE_AS_LIST = ANTARES_LICENSE_HEADER.splitlines() +LICENSE_TO_SAVE = [header + "\n" for header in LICENSE_AS_LIST] + ["\n"] + + +def is_license_file(filename: str) -> bool: + return LICENSE_FILE_PATTERN.match(filename) is not None + + +def check_file(file_path: Path, action: str) -> bool: + file_content = file_path.read_text().splitlines() + if len(file_content) >= 11 and file_content[:11] == LICENSE_AS_LIST: + return True + click.echo(f"{file_path} has no valid header.") + new_lines = [] + if action == "fix": + with open(file_path, "r") as f: # doesn't seem really optimal as I read the file twice. + already_licensed = False + lines = f.readlines() + first_line = lines[0].lower() if len(lines) > 0 else [] + if "copyright" in first_line or "license" in first_line: # assumes license follows this + already_licensed = True + if already_licensed: # I don't really know what to do here + raise ValueError(f"File {file_path} already licensed.") + else: + new_lines = LICENSE_TO_SAVE + lines + if new_lines: + with open(file_path, "w") as f: + f.writelines(new_lines) + + +def check_dir(cwd: Path, dir_path: Path, action: str, invalid_files: List[Path]) -> None: + _, dirnames, filenames = next(os.walk(dir_path)) + for f in filenames: + if dir_path != cwd and is_license_file(f): + click.echo(f"Found third party license file, skipping folder: {dir_path / f}") + return + + for f in filenames: + file_path = dir_path / f + + if file_path.suffixes != [".py"]: + continue + + if not check_file(file_path, action): + invalid_files.append(file_path) + + for d in dirnames: + check_dir(cwd, dir_path / d, action, invalid_files) + @click.command("license_checker_and_adder") @click.option( "--path", nargs=1, required=True, - type=click.Path(exists=True), + type=click.Path(exists=True, path_type=Path), help="Path to check", ) @click.option( @@ -25,49 +91,10 @@ def cli(path: Path, action: str) -> None: if action not in ["check", "check-strict", "fix"]: raise ValueError(f"Parameter --action should be 'check', 'check-strict' or 'fix' and was '{action}'") - license_header = """# Copyright (c) 2024, RTE (https://www.rte-france.com) -# -# See AUTHORS.txt -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# SPDX-License-Identifier: MPL-2.0 -# -# This file is part of the Antares project. -""" - license_as_list = license_header.splitlines() - license_to_save = [header + "\n" for header in license_as_list] - license_to_save.append("\n") - - file_count = 0 - all_files_and_folder = glob.glob(os.path.join(path, "**"), recursive=True) - for file in all_files_and_folder: - file_path = Path(file) - if not file_path.is_file(): - continue - if file_path.suffixes != [".py"]: - continue - file_content = file_path.read_text().splitlines() - if len(file_content) < 11 or file_content[:11] != license_as_list: - file_count += 1 - click.echo(f"{file_path.relative_to(path)} has no valid header.") - new_lines = [] - if action == "fix": - with open(file_path, "r") as f: # doesn't seem really optimal as I read the file twice. - already_licensed = False - lines = f.readlines() - first_line = lines[0].lower() if len(lines) > 0 else [] - if "copyright" in first_line or "license" in first_line: # assumes license follows this - already_licensed = True - if already_licensed: # I don't really know what to do here - raise ValueError(f"File {file_path.relative_to(path)} already licensed.") - else: - new_lines = license_to_save + lines - if new_lines: - with open(file_path, "w") as f: - f.writelines(new_lines) + invalid_files = [] + cwd = Path.cwd() + check_dir(cwd, path, action, invalid_files) + file_count = len(invalid_files) if file_count > 0: if action == "fix": click.echo(f"{file_count} files have been fixed") From b5a3828d911ce0abec37494aaefd16b028fce0dc Mon Sep 17 00:00:00 2001 From: Sylvain Leclerc Date: Tue, 3 Sep 2024 16:36:01 +0200 Subject: [PATCH 2/4] Remove duplicate imports Signed-off-by: Sylvain Leclerc --- antarest/utils.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/antarest/utils.py b/antarest/utils.py index c0ecffbcca..c1a6c9166c 100644 --- a/antarest/utils.py +++ b/antarest/utils.py @@ -20,17 +20,9 @@ import uvicorn # type: ignore from fastapi import FastAPI from ratelimit import RateLimitMiddleware # type: ignore -from ratelimit import RateLimitMiddleware # type: ignore -from ratelimit.backends.redis import RedisBackend # type: ignore -from ratelimit.backends.redis import RedisBackend # type: ignore -from ratelimit.backends.redis import RedisBackend # type: ignore from ratelimit.backends.redis import RedisBackend # type: ignore from ratelimit.backends.simple import MemoryBackend # type: ignore -from ratelimit.backends.simple import MemoryBackend # type: ignore -from ratelimit.backends.simple import MemoryBackend # type: ignore -from ratelimit.backends.simple import MemoryBackend # type: ignore -from sqlalchemy import create_engine # type: ignore -from sqlalchemy import create_engine # type: ignore +from sqlalchemy import create_engine from sqlalchemy.engine.base import Engine # type: ignore from sqlalchemy.pool import NullPool # type: ignore From 31cb8aa2a00dc893f68c328604b9c232abf8b5bb Mon Sep 17 00:00:00 2001 From: Sylvain Leclerc Date: Tue, 3 Sep 2024 16:53:51 +0200 Subject: [PATCH 3/4] Remove unwanted file Signed-off-by: Sylvain Leclerc --- antarest/fastapi_jwt_auth/README.md | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 antarest/fastapi_jwt_auth/README.md diff --git a/antarest/fastapi_jwt_auth/README.md b/antarest/fastapi_jwt_auth/README.md deleted file mode 100644 index c4dfd9017e..0000000000 --- a/antarest/fastapi_jwt_auth/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# FastAPI JWT Auth - -Forked from https://github.com/IndominusByte/fastapi-jwt-auth, -licensed under MIT license, see [LICENSE](LICENSE). From 502d0d6e765e69280e55669438979aa63fd6071e Mon Sep 17 00:00:00 2001 From: Sylvain Leclerc Date: Tue, 3 Sep 2024 16:56:16 +0200 Subject: [PATCH 4/4] Fix import sort order Signed-off-by: Sylvain Leclerc --- antarest/eventbus/web.py | 2 +- antarest/login/auth.py | 2 +- antarest/login/main.py | 2 +- antarest/login/web.py | 2 +- antarest/main.py | 2 +- antarest/utils.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/antarest/eventbus/web.py b/antarest/eventbus/web.py index d316fa0c94..481db7b371 100644 --- a/antarest/eventbus/web.py +++ b/antarest/eventbus/web.py @@ -18,6 +18,7 @@ from typing import List, Optional from fastapi import Depends, FastAPI, HTTPException, Query +from fastapi_jwt_auth import AuthJWT # type: ignore from pydantic import BaseModel from starlette.websockets import WebSocket, WebSocketDisconnect @@ -27,7 +28,6 @@ from antarest.core.model import PermissionInfo, StudyPermissionType from antarest.core.permissions import check_permission from antarest.login.auth import Auth -from fastapi_jwt_auth import AuthJWT # type: ignore logger = logging.getLogger(__name__) diff --git a/antarest/login/auth.py b/antarest/login/auth.py index 615dc7e170..6cd2a3139f 100644 --- a/antarest/login/auth.py +++ b/antarest/login/auth.py @@ -16,13 +16,13 @@ from typing import Any, Callable, Coroutine, Dict, Optional, Tuple, Union from fastapi import Depends +from fastapi_jwt_auth import AuthJWT # type: ignore from pydantic import BaseModel from ratelimit.types import Scope # type: ignore from starlette.requests import Request from antarest.core.config import Config from antarest.core.jwt import DEFAULT_ADMIN_USER, JWTUser -from fastapi_jwt_auth import AuthJWT # type: ignore logger = logging.getLogger(__name__) diff --git a/antarest/login/main.py b/antarest/login/main.py index b0b96c760a..fe1acea550 100644 --- a/antarest/login/main.py +++ b/antarest/login/main.py @@ -15,6 +15,7 @@ from typing import Any, Optional from fastapi import FastAPI +from fastapi_jwt_auth import AuthJWT # type: ignore from fastapi_jwt_auth.exceptions import AuthJWTException # type: ignore from starlette.requests import Request from starlette.responses import JSONResponse @@ -26,7 +27,6 @@ from antarest.login.repository import BotRepository, GroupRepository, RoleRepository, UserLdapRepository, UserRepository from antarest.login.service import LoginService from antarest.login.web import create_login_api -from fastapi_jwt_auth import AuthJWT # type: ignore def build_login( diff --git a/antarest/login/web.py b/antarest/login/web.py index 508db2b77a..8f98012529 100644 --- a/antarest/login/web.py +++ b/antarest/login/web.py @@ -16,6 +16,7 @@ from typing import Any, List, Optional, Union from fastapi import APIRouter, Depends, HTTPException +from fastapi_jwt_auth import AuthJWT # type: ignore from markupsafe import escape from pydantic import BaseModel @@ -41,7 +42,6 @@ UserInfo, ) from antarest.login.service import LoginService -from fastapi_jwt_auth import AuthJWT # type: ignore logger = logging.getLogger(__name__) diff --git a/antarest/main.py b/antarest/main.py index 5b3ea2cb18..c5e4d30197 100644 --- a/antarest/main.py +++ b/antarest/main.py @@ -23,6 +23,7 @@ from fastapi import FastAPI, HTTPException from fastapi.encoders import jsonable_encoder from fastapi.exceptions import RequestValidationError +from fastapi_jwt_auth import AuthJWT # type: ignore from ratelimit import RateLimitMiddleware # type: ignore from ratelimit.backends.redis import RedisBackend # type: ignore from ratelimit.backends.simple import MemoryBackend # type: ignore @@ -53,7 +54,6 @@ from antarest.study.storage.rawstudy.watcher import Watcher from antarest.tools.admin_lib import clean_locks from antarest.utils import SESSION_ARGS, Module, create_services, init_db_engine -from fastapi_jwt_auth import AuthJWT # type: ignore logger = logging.getLogger(__name__) diff --git a/antarest/utils.py b/antarest/utils.py index c1a6c9166c..eb2e5dc479 100644 --- a/antarest/utils.py +++ b/antarest/utils.py @@ -19,6 +19,7 @@ import sqlalchemy.ext.baked # type: ignore import uvicorn # type: ignore from fastapi import FastAPI +from fastapi_jwt_auth import AuthJWT # type: ignore from ratelimit import RateLimitMiddleware # type: ignore from ratelimit.backends.redis import RedisBackend # type: ignore from ratelimit.backends.simple import MemoryBackend # type: ignore @@ -52,7 +53,6 @@ from antarest.worker.archive_worker import ArchiveWorker from antarest.worker.simulator_worker import SimulatorWorker from antarest.worker.worker import AbstractWorker -from fastapi_jwt_auth import AuthJWT # type: ignore logger = logging.getLogger(__name__)