Repository: conan-io/conan
Pull request #16648 (2024-07-10 16:41:53)

refactor JWT and make test less flaky
Changelog: Omit
Docs: Omit



Patch:
diff --git a/conan/tools/cmake/layout.py b/conan/tools/cmake/layout.py
index dad6ce312..fbff89c20 100644
--- a/conan/tools/cmake/layout.py
+++ b/conan/tools/cmake/layout.py
@@ -94,9 +94,11 @@ def get_build_folder_custom_vars(conanfile):
                     tmp = "{}_{}".format(var, value)
         elif group == "self":
             tmp = getattr(conanfile, var, None)
+        elif group == "const":
+            tmp = var
         else:
             raise ConanException("Invalid 'tools.cmake.cmake_layout:build_folder_vars' value, it has"
-                                 " to start with 'settings.' or 'options.': {}".format(s))
+                                 f" to start with 'settings.', 'options.', 'self.' or 'const.': {s}")
         if tmp:
             ret.append(tmp.lower())
 
diff --git a/conan/tools/cmake/toolchain/blocks.py b/conan/tools/cmake/toolchain/blocks.py
index 0d6585718..425e62b2a 100644
--- a/conan/tools/cmake/toolchain/blocks.py
+++ b/conan/tools/cmake/toolchain/blocks.py
@@ -724,7 +724,7 @@ class ExtraFlagsBlock(Block):
         {% if defines %}
         {% if config %}
         {% for define in defines %}
-        add_compile_definitions($<$<CONFIG:{{config}}>:"{{ define }}">)
+        add_compile_definitions("$<$<CONFIG:{{config}}>:{{ define }}>")
         {% endfor %}
         {% else %}
         add_compile_definitions({% for define in defines %} "{{ define }}"{% endfor %})
diff --git a/conan/tools/gnu/gnutoolchain.py b/conan/tools/gnu/gnutoolchain.py
index 95c32129e..9848e18fc 100644
--- a/conan/tools/gnu/gnutoolchain.py
+++ b/conan/tools/gnu/gnutoolchain.py
@@ -123,15 +123,13 @@ class GnuToolchain:
         if is_msvc(self._conanfile):
             extra_env_vars = {"CC": "cl -nologo",
                               "CXX": "cl -nologo",
-                              "LD": "link -nologo",
-                              "AR": "lib",
                               "NM": "dumpbin -symbols",
                               "OBJDUMP": ":",
                               "RANLIB": ":",
                               "STRIP": ":"}
         # Configuration map
         compilers_mapping = {"c": "CC", "cpp": "CXX", "cuda": "NVCC", "fortran": "FC",
-                             "rc": "RC", "ld": "LD", "ar": "AR", "nm": "NM", "ranlib": "RANLIB",
+                             "rc": "RC", "nm": "NM", "ranlib": "RANLIB",
                              "objdump": "OBJDUMP", "strip": "STRIP"}
         # Compiler definitions by conf
         compilers_by_conf = self._conanfile.conf.get("tools.build:compiler_executables", default={},
diff --git a/conans/model/conf.py b/conans/model/conf.py
index 1a36d24a5..e56541cfa 100644
--- a/conans/model/conf.py
+++ b/conans/model/conf.py
@@ -117,7 +117,7 @@ BUILT_IN_CONFS = {
     "tools.apple:enable_visibility": "(boolean) Enable/Disable Visibility Apple Clang flags",
     "tools.env.virtualenv:powershell": "If it is set to True it will generate powershell launchers if os=Windows",
     # Compilers/Flags configurations
-    "tools.build:compiler_executables": "Defines a Python dict-like with the compilers path to be used. Allowed keys {'c', 'cpp', 'cuda', 'objc', 'objcxx', 'rc', 'fortran', 'asm', 'hip', 'ispc', 'ld', 'ar'}",
+    "tools.build:compiler_executables": "Defines a Python dict-like with the compilers path to be used. Allowed keys {'c', 'cpp', 'cuda', 'objc', 'objcxx', 'rc', 'fortran', 'asm', 'hip', 'ispc'}",
     "tools.build:cxxflags": "List of extra CXX flags used by different toolchains like CMakeToolchain, AutotoolsToolchain and MesonToolchain",
     "tools.build:cflags": "List of extra C flags used by different toolchains like CMakeToolchain, AutotoolsToolchain and MesonToolchain",
     "tools.build:defines": "List of extra definition flags used by different toolchains like CMakeToolchain, AutotoolsToolchain and MesonToolchain",
diff --git a/conans/server/crypto/jwt/jwt_credentials_manager.py b/conans/server/crypto/jwt/jwt_credentials_manager.py
index f2699f887..609469875 100644
--- a/conans/server/crypto/jwt/jwt_credentials_manager.py
+++ b/conans/server/crypto/jwt/jwt_credentials_manager.py
@@ -1,19 +1,26 @@
-from conans.server.crypto.jwt.jwt_manager import JWTManager
+from datetime import datetime, timezone
+from calendar import timegm
 
+import jwt
 
-class JWTCredentialsManager(JWTManager):
+
+class JWTCredentialsManager:
     """JWT for manage auth credentials"""
 
     def __init__(self, secret, expire_time):
-        super(JWTCredentialsManager, self).__init__(secret, expire_time)
+        """expire_time is a timedelta
+        secret is a string with the secret encoding key"""
+        self.secret = secret
+        self.expire_time = expire_time
 
-    def get_token_for(self, brl_user):
+    def get_token_for(self, user):
         """Generates a token with the brl_user and additional data dict if needed"""
-        return JWTManager.get_token_for(self, {"user": brl_user})
+        profile_fields = {"user": user,
+                          "exp": timegm((datetime.now(timezone.utc) + self.expire_time).timetuple())}
+        return jwt.encode(profile_fields, self.secret, algorithm="HS256")
 
     def get_user(self, token):
         """Gets the user from credentials object. None if no credentials.
         Can raise jwt.ExpiredSignature and jwt.DecodeError"""
-        profile = self.get_profile(token)
-        username = profile.get("user", None)
-        return username
+        profile = jwt.decode(token, self.secret, algorithms=["HS256"])
+        return profile.get("user", None)
diff --git a/conans/server/crypto/jwt/jwt_manager.py b/conans/server/crypto/jwt/jwt_manager.py
deleted file mode 100644
index fadd972f2..000000000
--- a/conans/server/crypto/jwt/jwt_manager.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from datetime import datetime, timezone
-from calendar import timegm
-
-import jwt
-
-
-class JWTManager:
-    """
-        Handles the JWT token generation and encryption.
-    """
-
-    def __init__(self, secret, expire_time):
-        """expire_time is a timedelta
-           secret is a string with the secret encoding key"""
-        self.secret = secret
-        self.expire_time = expire_time
-
-    def get_token_for(self, profile_fields=None):
-        """Generates a token with the provided fields.
-        if exp is True, expiration time will be used"""
-        profile_fields = profile_fields or {}
-
-        if self.expire_time:
-            profile_fields["exp"] = timegm((datetime.now(timezone.utc)
-                                            + self.expire_time).timetuple())
-
-        return jwt.encode(profile_fields, self.secret, algorithm="HS256")
-
-    def get_profile(self, token):
-        """Gets the user from credentials object. None if no credentials.
-        Can raise jwt.ExpiredSignature and jwt.DecodeError"""
-        profile = jwt.decode(token, self.secret, algorithms=["HS256"])
-        return profile


Test patch:
diff --git a/test/functional/toolchains/cmake/test_cmake_toolchain.py b/test/functional/toolchains/cmake/test_cmake_toolchain.py
index acd6c5ee1..b64c5c791 100644
--- a/test/functional/toolchains/cmake/test_cmake_toolchain.py
+++ b/test/functional/toolchains/cmake/test_cmake_toolchain.py
@@ -1913,3 +1913,106 @@ def test_cmake_toolchain_cxxflags_multi_config():
     assert 'DEFINE conan_test_answer=123' in c.out
     assert 'other=' not in c.out
     assert "CPLUSPLUS: __cplusplus19" in c.out
+
+
+@pytest.mark.tool("ninja")
+@pytest.mark.tool("cmake", "3.23")
+def test_cmake_toolchain_ninja_multi_config():
+    c = TestClient()
+    profile_release = textwrap.dedent(r"""
+        include(default)
+        [conf]
+        tools.cmake.cmaketoolchain:generator=Ninja Multi-Config
+        tools.build:defines=["conan_test_answer=42", "conan_test_other=24"]
+        """)
+    profile_debug = textwrap.dedent(r"""
+        include(default)
+        [settings]
+        build_type=Debug
+        [conf]
+        tools.cmake.cmaketoolchain:generator=Ninja Multi-Config
+        tools.build:defines=["conan_test_answer=123"]
+        """)
+    profile_relwithdebinfo = textwrap.dedent(r"""
+        include(default)
+        [settings]
+        build_type=RelWithDebInfo
+        [conf]
+        tools.cmake.cmaketoolchain:generator=Ninja Multi-Config
+        tools.build:defines=["conan_test_answer=456", "conan_test_other=abc", 'conan_test_complex="1 2"']
+        """)
+
+    conanfile = textwrap.dedent(r'''
+        from conan import ConanFile
+        from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
+
+        class Test(ConanFile):
+            exports_sources = "CMakeLists.txt", "src/*"
+            settings = "os", "compiler", "arch", "build_type"
+            generators = "CMakeToolchain"
+
+            def layout(self):
+                cmake_layout(self)
+
+            def build(self):
+                cmake = CMake(self)
+                cmake.configure()
+                cmake.build()
+        ''')
+
+    main = textwrap.dedent(r"""
+        #include <iostream>
+        #include <stdio.h>
+
+        #define STR(x)   #x
+        #define SHOW_DEFINE(x) printf("DEFINE %s=%s!\n", #x, STR(x))
+
+        int main() {
+            SHOW_DEFINE(conan_test_answer);
+            #ifdef conan_test_other
+            SHOW_DEFINE(conan_test_other);
+            #endif
+            #ifdef conan_test_complex
+            SHOW_DEFINE(conan_test_complex);
+            #endif
+        }
+        """)
+
+    cmakelists = textwrap.dedent("""
+        cmake_minimum_required(VERSION 3.15)
+        project(Test CXX)
+        add_executable(example src/main.cpp)
+        """)
+
+    c.save({"conanfile.py": conanfile,
+            "profile_release": profile_release,
+            "profile_debug": profile_debug,
+            "profile_relwithdebinfo": profile_relwithdebinfo,
+            "src/main.cpp": main,
+            "CMakeLists.txt": cmakelists})
+    c.run("install . -pr=./profile_release")
+    c.run("install . -pr=./profile_debug")
+    c.run("install . -pr=./profile_relwithdebinfo")
+
+    with c.chdir("build"):
+        env = r".\generators\conanbuild.bat &&" if platform.system() == "Windows" else ""
+        c.run_command(f"{env} cmake .. -G \"Ninja Multi-Config\" "
+                      "-DCMAKE_TOOLCHAIN_FILE=generators/conan_toolchain.cmake")
+        c.run_command(f"{env} cmake --build . --config Release")
+        c.run_command(f"{env} cmake --build . --config Debug")
+        c.run_command(f"{env} cmake --build . --config RelWithDebInfo")
+
+    c.run_command(os.sep.join([".", "build", "Release", "example"]))
+    assert 'DEFINE conan_test_answer=42!' in c.out
+    assert 'DEFINE conan_test_other=24!' in c.out
+    assert 'complex=' not in c.out
+
+    c.run_command(os.sep.join([".", "build", "Debug", "example"]))
+    assert 'DEFINE conan_test_answer=123' in c.out
+    assert 'other=' not in c.out
+    assert 'complex=' not in c.out
+
+    c.run_command(os.sep.join([".", "build", "RelWithDebInfo", "example"]))
+    assert 'DEFINE conan_test_answer=456!' in c.out
+    assert 'DEFINE conan_test_other=abc!' in c.out
+    assert 'DEFINE conan_test_complex="1 2"!' in c.out
diff --git a/test/integration/command/user_test.py b/test/integration/command/user_test.py
index b5e4857a1..960ba8640 100644
--- a/test/integration/command/user_test.py
+++ b/test/integration/command/user_test.py
@@ -418,7 +418,7 @@ class TestRemoteAuth:
 
     def test_remote_auth_server_expire_token(self):
         server = TestServer(users={"myuser": "password", "myotheruser": "otherpass"})
-        server.test_server.ra.api_v2.credentials_manager.expire_time = timedelta(seconds=1)
+        server.test_server.ra.api_v2.credentials_manager.expire_time = timedelta(seconds=2)
         c = TestClient(servers={"default": server}, inputs=["myuser", "password",
                                                             "myotheruser", "otherpass",
                                                             "user", "pass", "user", "pass",
@@ -429,10 +429,10 @@ class TestRemoteAuth:
         c.run("remote auth *")
         assert "user: myuser" in c.out
         # Token should expire
-        time.sleep(2)
+        time.sleep(2.5)
         c.run("remote auth *")
         assert "user: myotheruser" in c.out
         # Token should expire
-        time.sleep(2)
+        time.sleep(2.5)
         c.run("remote auth *")
         assert "error: Too many failed login attempts, bye!" in c.out
diff --git a/test/integration/toolchains/cmake/test_cmaketoolchain.py b/test/integration/toolchains/cmake/test_cmaketoolchain.py
index dee27284e..38ac9049b 100644
--- a/test/integration/toolchains/cmake/test_cmaketoolchain.py
+++ b/test/integration/toolchains/cmake/test_cmaketoolchain.py
@@ -1278,6 +1278,32 @@ def test_build_folder_vars_self_name_version():
     assert "conan-windows-pkg-0.1-debug" in presets
 
 
+def test_build_folder_vars_constants_user():
+    c = TestClient()
+    conanfile = textwrap.dedent("""
+        from conan import ConanFile
+        from conan.tools.cmake import cmake_layout
+
+        class Conan(ConanFile):
+            name = "dep"
+            version = "0.1"
+            settings = "os", "build_type"
+            generators = "CMakeToolchain"
+
+            def layout(self):
+                cmake_layout(self)
+        """)
+    c.save({"conanfile.py": conanfile})
+    conf = "tools.cmake.cmake_layout:build_folder_vars='[\"const.myvalue\"]'"
+    settings = " -s os=FreeBSD -s arch=armv8 -s build_type=Debug"
+    c.run("install . -c={} {}".format(conf, settings))
+    assert "cmake --preset conan-myvalue-debug" in c.out
+    assert os.path.exists(os.path.join(c.current_folder, "build", "myvalue", "Debug"))
+    presets = load(os.path.join(c.current_folder,
+                                "build/myvalue/Debug/generators/CMakePresets.json"))
+    assert "conan-myvalue-debug" in presets
+
+
 def test_extra_flags():
     client = TestClient()
     conanfile = textwrap.dedent("""
diff --git a/test/integration/toolchains/gnu/test_gnutoolchain.py b/test/integration/toolchains/gnu/test_gnutoolchain.py
index 653599ae6..d35d45b04 100644
--- a/test/integration/toolchains/gnu/test_gnutoolchain.py
+++ b/test/integration/toolchains/gnu/test_gnutoolchain.py
@@ -381,8 +381,6 @@ def test_msvc_profile_defaults():
             # Default values and conf ones
             assert r'set "CC=clang"' in content  # conf value has precedence
             assert r'set "CXX=clang++"' in content  # conf value has precedence
-            assert 'set "LD=link -nologo"' in content
-            assert r'set "AR=lib"' in content
             assert 'set "NM=dumpbin -symbols"' in content
             assert 'set "OBJDUMP=:"' in content
             assert 'set "RANLIB=:"' in content
@@ -412,8 +410,6 @@ def test_msvc_profile_defaults():
             # Prepending compiler wrappers
             tc.extra_env.prepend("CC", "compile")
             tc.extra_env.prepend("CXX", "compile")
-            tc.extra_env.prepend("AR", "ar-lib")
-            tc.extra_env.append("LD", "-fixed")
             tc.extra_env.define("OBJDUMP", "other-value")
             tc.extra_env.unset("RANLIB")
             tc.generate()
@@ -424,8 +420,6 @@ def test_msvc_profile_defaults():
             # Default values
             assert r'set "CC=compile clang"' in content
             assert r'set "CXX=compile clang++"' in content
-            assert 'set "LD=link -nologo -fixed"' in content  # appended new value
-            assert r'set "AR=ar-lib lib"' in content
             assert 'set "NM=dumpbin -symbols"' in content
             assert 'set "OBJDUMP=other-value"' in content  # redefined
             assert 'set "RANLIB=:"' not in content  # removed
diff --git a/test/unittests/server/crypto/jwt_test.py b/test/unittests/server/crypto/jwt_test.py
index 9c6b916a4..d1253232b 100644
--- a/test/unittests/server/crypto/jwt_test.py
+++ b/test/unittests/server/crypto/jwt_test.py
@@ -1,40 +1,26 @@
 import time
-import unittest
 from datetime import timedelta
 
 import jwt
+import pytest
 from jwt import DecodeError
 
 from conans.server.crypto.jwt.jwt_credentials_manager import JWTCredentialsManager
-from conans.server.crypto.jwt.jwt_manager import JWTManager
 
 
-class JwtTest(unittest.TestCase):
+def test_jwt_manager():
+    # Instance the manager to generate tokens that expires in 10 ms
+    manager = JWTCredentialsManager(secret="1234asdf", expire_time=timedelta(seconds=1))
 
-    def setUp(self):
-        unittest.TestCase.setUp(self)
-        self.secret = "123123123qweqwe"
-        self.expire_time = timedelta(seconds=1)  # No lower resolution available
+    # Encrypt a profile
+    token = manager.get_token_for("myuser")
 
-    def test_jwt_manager(self):
-        # Instance the manager to generate tokens that expires in 10 ms
+    # Decrypt the profile
+    assert "myuser" == manager.get_user(token)
+    with pytest.raises(DecodeError):
+        manager.get_user("invalid_user")
 
-        manager = JWTManager(self.secret, self.expire_time)
-
-        # Encrypt a profile
-        profile = {"hello": "world"}
-        token = manager.get_token_for(profile)
-
-        # Decrypt the profile
-        decrypted_profile = manager.get_profile(token)
-        self.assertEqual(profile, decrypted_profile)
-
-        # Now wait 2 seconds and check if its valid now
-        time.sleep(2)
-        self.assertRaises(jwt.ExpiredSignatureError, manager.get_profile, token)
-
-    def test_jwt_credentials_manager(self):
-        manager = JWTCredentialsManager(self.secret, self.expire_time)
-        token = manager.get_token_for("lasote")
-        self.assertEqual(manager.get_user(token), "lasote")
-        self.assertRaises(DecodeError, manager.get_user, "invalid_user")
+    # Now wait 2 seconds and check if its valid now
+    time.sleep(2)
+    with pytest.raises(jwt.ExpiredSignatureError):
+        manager.get_user(token)