authentik.lib.tests.test_config

Test config loader

  1"""Test config loader"""
  2
  3import base64
  4from json import dumps
  5from os import chmod, environ, unlink, write
  6from tempfile import mkstemp
  7from unittest import mock
  8
  9from django.conf import ImproperlyConfigured
 10from django.test import TestCase
 11
 12from authentik.lib.config import (
 13    ENV_PREFIX,
 14    UNSET,
 15    Attr,
 16    AttrEncoder,
 17    ConfigLoader,
 18    django_db_config,
 19)
 20
 21
 22class TestConfig(TestCase):
 23    """Test config loader"""
 24
 25    check_deprecations_env_vars = {
 26        ENV_PREFIX + "_WORKER__CONCURRENCY": "2",
 27    }
 28
 29    @mock.patch.dict(environ, {ENV_PREFIX + "_test__test": "bar"})
 30    def test_env(self):
 31        """Test simple instance"""
 32        config = ConfigLoader()
 33        config.update_from_env()
 34        self.assertEqual(config.get("test.test"), "bar")
 35
 36    def test_patch(self):
 37        """Test patch decorator"""
 38        config = ConfigLoader()
 39        config.set("foo.bar", "bar")
 40        self.assertEqual(config.get("foo.bar"), "bar")
 41        with config.patch("foo.bar", "baz"):
 42            self.assertEqual(config.get("foo.bar"), "baz")
 43        self.assertEqual(config.get("foo.bar"), "bar")
 44
 45    @mock.patch.dict(environ, {"foo": "bar"})
 46    def test_uri_env(self):
 47        """Test URI parsing (environment)"""
 48        config = ConfigLoader()
 49        foo_uri = "env://foo"
 50        foo_parsed = config.parse_uri(foo_uri)
 51        self.assertEqual(foo_parsed.value, "bar")
 52        self.assertEqual(foo_parsed.source_type, Attr.Source.URI)
 53        self.assertEqual(foo_parsed.source, foo_uri)
 54        foo_bar_uri = "env://foo?bar"
 55        foo_bar_parsed = config.parse_uri(foo_bar_uri)
 56        self.assertEqual(foo_bar_parsed.value, "bar")
 57        self.assertEqual(foo_bar_parsed.source_type, Attr.Source.URI)
 58        self.assertEqual(foo_bar_parsed.source, foo_bar_uri)
 59
 60    def test_uri_file(self):
 61        """Test URI parsing (file load)"""
 62        config = ConfigLoader()
 63        file, file_name = mkstemp()
 64        write(file, b"foo")
 65        _, file2_name = mkstemp()
 66        chmod(file2_name, 0o000)  # Remove all permissions so we can't read the file
 67        self.assertEqual(config.parse_uri(f"file://{file_name}").value, "foo")
 68        self.assertEqual(config.parse_uri(f"file://{file2_name}?def").value, "def")
 69        unlink(file_name)
 70        unlink(file2_name)
 71
 72    def test_uri_file_update(self):
 73        """Test URI parsing (file load and update)"""
 74        file, file_name = mkstemp()
 75        write(file, b"foo")
 76        config = ConfigLoader(file_test=f"file://{file_name}")
 77        self.assertEqual(config.get("file_test"), "foo")
 78
 79        # Update config file
 80        write(file, b"bar")
 81        config.refresh("file_test")
 82        self.assertEqual(config.get("file_test"), "foobar")
 83
 84        unlink(file_name)
 85
 86    def test_uri_env_full(self):
 87        """Test URI set as env variable"""
 88        environ["AUTHENTIK_TEST_VAR"] = "file:///foo?bar"
 89        config = ConfigLoader()
 90        self.assertEqual(config.get("test_var"), "bar")
 91
 92    def test_file_update(self):
 93        """Test update_from_file"""
 94        config = ConfigLoader()
 95        file, file_name = mkstemp()
 96        write(file, b"{")
 97        file2, file2_name = mkstemp()
 98        write(file2, b"{")
 99        chmod(file2_name, 0o000)  # Remove all permissions so we can't read the file
100        with self.assertRaises(ImproperlyConfigured):
101            config.update_from_file(file_name)
102        config.update_from_file(file2_name)
103        unlink(file_name)
104        unlink(file2_name)
105
106    def test_get_int(self):
107        """Test get_int"""
108        config = ConfigLoader()
109        config.set("foo", 1234)
110        self.assertEqual(config.get_int("foo"), 1234)
111
112    def test_get_int_invalid(self):
113        """Test get_int"""
114        config = ConfigLoader()
115        config.set("foo", "bar")
116        self.assertEqual(config.get_int("foo", 1234), 1234)
117
118    def test_get_dict_from_b64_json(self):
119        """Test get_dict_from_b64_json"""
120        config = ConfigLoader()
121        test_value = b'  { "foo": "bar"   }   '
122        b64_value = base64.b64encode(test_value)
123        config.set("foo", b64_value)
124        self.assertEqual(config.get_dict_from_b64_json("foo"), {"foo": "bar"})
125
126    def test_get_dict_from_b64_json_missing_brackets(self):
127        """Test get_dict_from_b64_json with missing brackets"""
128        config = ConfigLoader()
129        test_value = b' "foo": "bar"     '
130        b64_value = base64.b64encode(test_value)
131        config.set("foo", b64_value)
132        self.assertEqual(config.get_dict_from_b64_json("foo"), {})
133
134    def test_get_dict_from_b64_json_invalid(self):
135        """Test get_dict_from_b64_json with invalid value"""
136        config = ConfigLoader()
137        config.set("foo", "bar")
138        self.assertEqual(config.get_dict_from_b64_json("foo"), {})
139
140    def test_attr_json_encoder(self):
141        """Test AttrEncoder"""
142        test_attr = Attr("foo", Attr.Source.ENV, "AUTHENTIK_POSTGRESQL__USERNAME")
143        json_attr = dumps(test_attr, indent=4, cls=AttrEncoder)
144        self.assertEqual(json_attr, '"foo"')
145
146    def test_attr_json_encoder_no_attr(self):
147        """Test AttrEncoder if no Attr is passed"""
148
149        class Test:
150            """Non Attr class"""
151
152        with self.assertRaises(TypeError):
153            test_obj = Test()
154            dumps(test_obj, indent=4, cls=AttrEncoder)
155
156    def test_get_optional_int(self):
157        config = ConfigLoader()
158        self.assertEqual(config.get_optional_int("foo", 21), 21)
159        self.assertEqual(config.get_optional_int("foo"), None)
160        config.set("foo", "21")
161        self.assertEqual(config.get_optional_int("foo"), 21)
162        self.assertEqual(config.get_optional_int("foo", 0), 21)
163        self.assertEqual(config.get_optional_int("foo", "null"), 21)
164        config.set("foo", "null")
165        self.assertEqual(config.get_optional_int("foo"), None)
166        self.assertEqual(config.get_optional_int("foo", 21), None)
167
168    @mock.patch.dict(environ, check_deprecations_env_vars)
169    def test_check_deprecations(self):
170        """Test config key re-write for deprecated env vars"""
171        config = ConfigLoader()
172        config.update_from_env()
173        config.check_deprecations()
174        self.assertEqual(config.get("worker.concurrency", UNSET), UNSET)
175        self.assertEqual(config.get("worker.threads"), 2)
176
177    def test_get_keys(self):
178        """Test get_keys"""
179        config = ConfigLoader()
180        config.set("foo.bar", "baz")
181        self.assertEqual(list(config.get_keys("foo")), ["bar"])
182
183    def test_db_default(self):
184        """Test default DB Config"""
185        config = ConfigLoader()
186        config.set("postgresql.host", "foo")
187        config.set("postgresql.name", "foo")
188        config.set("postgresql.user", "foo")
189        config.set("postgresql.password", "foo")
190        config.set("postgresql.port", "foo")
191        config.set("postgresql.sslmode", "foo")
192        config.set("postgresql.sslrootcert", "foo")
193        config.set("postgresql.sslcert", "foo")
194        config.set("postgresql.sslkey", "foo")
195        config.set("postgresql.test.name", "foo")
196        conf = django_db_config(config)
197        self.assertEqual(
198            conf,
199            {
200                "default": {
201                    "ENGINE": "psqlextra.backend",
202                    "HOST": "foo",
203                    "NAME": "foo",
204                    "OPTIONS": {
205                        "pool": False,
206                        "sslcert": "foo",
207                        "sslkey": "foo",
208                        "sslmode": "foo",
209                        "sslrootcert": "foo",
210                    },
211                    "PASSWORD": "foo",
212                    "PORT": "foo",
213                    "TEST": {"NAME": "foo"},
214                    "USER": "foo",
215                    "CONN_MAX_AGE": 0,
216                    "CONN_HEALTH_CHECKS": False,
217                    "DISABLE_SERVER_SIDE_CURSORS": False,
218                }
219            },
220        )
221
222    def test_db_conn_max_age(self):
223        """Test DB conn_max_age Config"""
224        config = ConfigLoader()
225        config.set("postgresql.conn_max_age", "null")
226        conf = django_db_config(config)
227        self.assertEqual(
228            conf["default"]["CONN_MAX_AGE"],
229            None,
230        )
231
232    def test_db_read_replicas(self):
233        """Test read replicas"""
234        config = ConfigLoader()
235        config.set("postgresql.host", "foo")
236        config.set("postgresql.name", "foo")
237        config.set("postgresql.user", "foo")
238        config.set("postgresql.password", "foo")
239        config.set("postgresql.port", "foo")
240        config.set("postgresql.sslmode", "foo")
241        config.set("postgresql.sslrootcert", "foo")
242        config.set("postgresql.sslcert", "foo")
243        config.set("postgresql.sslkey", "foo")
244        config.set("postgresql.test.name", "foo")
245        # Read replica
246        config.set("postgresql.read_replicas.0.host", "bar")
247        conf = django_db_config(config)
248        self.assertEqual(
249            conf,
250            {
251                "default": {
252                    "ENGINE": "psqlextra.backend",
253                    "HOST": "foo",
254                    "NAME": "foo",
255                    "OPTIONS": {
256                        "pool": False,
257                        "sslcert": "foo",
258                        "sslkey": "foo",
259                        "sslmode": "foo",
260                        "sslrootcert": "foo",
261                    },
262                    "PASSWORD": "foo",
263                    "PORT": "foo",
264                    "TEST": {"NAME": "foo"},
265                    "USER": "foo",
266                    "CONN_MAX_AGE": 0,
267                    "CONN_HEALTH_CHECKS": False,
268                    "DISABLE_SERVER_SIDE_CURSORS": False,
269                },
270                "replica_0": {
271                    "ENGINE": "psqlextra.backend",
272                    "HOST": "bar",
273                    "NAME": "foo",
274                    "OPTIONS": {
275                        "pool": False,
276                        "sslcert": "foo",
277                        "sslkey": "foo",
278                        "sslmode": "foo",
279                        "sslrootcert": "foo",
280                    },
281                    "PASSWORD": "foo",
282                    "PORT": "foo",
283                    "TEST": {"NAME": "foo"},
284                    "USER": "foo",
285                    "CONN_MAX_AGE": 0,
286                    "CONN_HEALTH_CHECKS": False,
287                    "DISABLE_SERVER_SIDE_CURSORS": False,
288                },
289            },
290        )
291
292    def test_db_read_replicas_pgbouncer(self):
293        """Test read replicas"""
294        config = ConfigLoader()
295        config.set("postgresql.host", "foo")
296        config.set("postgresql.name", "foo")
297        config.set("postgresql.user", "foo")
298        config.set("postgresql.password", "foo")
299        config.set("postgresql.port", "foo")
300        config.set("postgresql.sslmode", "foo")
301        config.set("postgresql.sslrootcert", "foo")
302        config.set("postgresql.sslcert", "foo")
303        config.set("postgresql.sslkey", "foo")
304        config.set("postgresql.test.name", "foo")
305        config.set("postgresql.use_pgbouncer", True)
306        # Read replica
307        config.set("postgresql.read_replicas.0.host", "bar")
308        # Override conn_max_age
309        config.set("postgresql.read_replicas.0.conn_max_age", 10)
310        # This isn't supported
311        config.set("postgresql.read_replicas.0.use_pgbouncer", False)
312        conf = django_db_config(config)
313        self.assertEqual(
314            conf,
315            {
316                "default": {
317                    "DISABLE_SERVER_SIDE_CURSORS": True,
318                    "CONN_MAX_AGE": None,
319                    "CONN_HEALTH_CHECKS": False,
320                    "ENGINE": "psqlextra.backend",
321                    "HOST": "foo",
322                    "NAME": "foo",
323                    "OPTIONS": {
324                        "pool": False,
325                        "sslcert": "foo",
326                        "sslkey": "foo",
327                        "sslmode": "foo",
328                        "sslrootcert": "foo",
329                    },
330                    "PASSWORD": "foo",
331                    "PORT": "foo",
332                    "TEST": {"NAME": "foo"},
333                    "USER": "foo",
334                },
335                "replica_0": {
336                    "DISABLE_SERVER_SIDE_CURSORS": True,
337                    "CONN_MAX_AGE": 10,
338                    "CONN_HEALTH_CHECKS": False,
339                    "ENGINE": "psqlextra.backend",
340                    "HOST": "bar",
341                    "NAME": "foo",
342                    "OPTIONS": {
343                        "pool": False,
344                        "sslcert": "foo",
345                        "sslkey": "foo",
346                        "sslmode": "foo",
347                        "sslrootcert": "foo",
348                    },
349                    "PASSWORD": "foo",
350                    "PORT": "foo",
351                    "TEST": {"NAME": "foo"},
352                    "USER": "foo",
353                },
354            },
355        )
356
357    def test_db_read_replicas_pgpool(self):
358        """Test read replicas"""
359        config = ConfigLoader()
360        config.set("postgresql.host", "foo")
361        config.set("postgresql.name", "foo")
362        config.set("postgresql.user", "foo")
363        config.set("postgresql.password", "foo")
364        config.set("postgresql.port", "foo")
365        config.set("postgresql.sslmode", "foo")
366        config.set("postgresql.sslrootcert", "foo")
367        config.set("postgresql.sslcert", "foo")
368        config.set("postgresql.sslkey", "foo")
369        config.set("postgresql.test.name", "foo")
370        config.set("postgresql.use_pgpool", True)
371        # Read replica
372        config.set("postgresql.read_replicas.0.host", "bar")
373        # This isn't supported
374        config.set("postgresql.read_replicas.0.use_pgpool", False)
375        conf = django_db_config(config)
376        self.assertEqual(
377            conf,
378            {
379                "default": {
380                    "DISABLE_SERVER_SIDE_CURSORS": True,
381                    "CONN_MAX_AGE": 0,
382                    "CONN_HEALTH_CHECKS": False,
383                    "ENGINE": "psqlextra.backend",
384                    "HOST": "foo",
385                    "NAME": "foo",
386                    "OPTIONS": {
387                        "pool": False,
388                        "sslcert": "foo",
389                        "sslkey": "foo",
390                        "sslmode": "foo",
391                        "sslrootcert": "foo",
392                    },
393                    "PASSWORD": "foo",
394                    "PORT": "foo",
395                    "TEST": {"NAME": "foo"},
396                    "USER": "foo",
397                },
398                "replica_0": {
399                    "DISABLE_SERVER_SIDE_CURSORS": True,
400                    "CONN_MAX_AGE": 0,
401                    "CONN_HEALTH_CHECKS": False,
402                    "ENGINE": "psqlextra.backend",
403                    "HOST": "bar",
404                    "NAME": "foo",
405                    "OPTIONS": {
406                        "pool": False,
407                        "sslcert": "foo",
408                        "sslkey": "foo",
409                        "sslmode": "foo",
410                        "sslrootcert": "foo",
411                    },
412                    "PASSWORD": "foo",
413                    "PORT": "foo",
414                    "TEST": {"NAME": "foo"},
415                    "USER": "foo",
416                },
417            },
418        )
419
420    def test_db_read_replicas_diff_ssl(self):
421        """Test read replicas (with different SSL Settings)"""
422        """Test read replicas"""
423        config = ConfigLoader()
424        config.set("postgresql.host", "foo")
425        config.set("postgresql.name", "foo")
426        config.set("postgresql.user", "foo")
427        config.set("postgresql.password", "foo")
428        config.set("postgresql.port", "foo")
429        config.set("postgresql.sslmode", "foo")
430        config.set("postgresql.sslrootcert", "foo")
431        config.set("postgresql.sslcert", "foo")
432        config.set("postgresql.sslkey", "foo")
433        config.set("postgresql.test.name", "foo")
434        # Read replica
435        config.set("postgresql.read_replicas.0.host", "bar")
436        config.set("postgresql.read_replicas.0.sslcert", "bar")
437        conf = django_db_config(config)
438        self.assertEqual(
439            conf,
440            {
441                "default": {
442                    "ENGINE": "psqlextra.backend",
443                    "HOST": "foo",
444                    "NAME": "foo",
445                    "OPTIONS": {
446                        "pool": False,
447                        "sslcert": "foo",
448                        "sslkey": "foo",
449                        "sslmode": "foo",
450                        "sslrootcert": "foo",
451                    },
452                    "PASSWORD": "foo",
453                    "PORT": "foo",
454                    "TEST": {"NAME": "foo"},
455                    "USER": "foo",
456                    "DISABLE_SERVER_SIDE_CURSORS": False,
457                    "CONN_MAX_AGE": 0,
458                    "CONN_HEALTH_CHECKS": False,
459                },
460                "replica_0": {
461                    "ENGINE": "psqlextra.backend",
462                    "HOST": "bar",
463                    "NAME": "foo",
464                    "OPTIONS": {
465                        "pool": False,
466                        "sslcert": "bar",
467                        "sslkey": "foo",
468                        "sslmode": "foo",
469                        "sslrootcert": "foo",
470                    },
471                    "PASSWORD": "foo",
472                    "PORT": "foo",
473                    "TEST": {"NAME": "foo"},
474                    "USER": "foo",
475                    "DISABLE_SERVER_SIDE_CURSORS": False,
476                    "CONN_MAX_AGE": 0,
477                    "CONN_HEALTH_CHECKS": False,
478                },
479            },
480        )
481
482    def test_db_conn_options(self):
483        config = ConfigLoader()
484        config.set(
485            "postgresql.conn_options",
486            base64.b64encode(
487                dumps(
488                    {
489                        "connect_timeout": "10",
490                    }
491                ).encode()
492            ).decode(),
493        )
494        config.set("postgresql.read_replicas.0.host", "bar")
495
496        conf = django_db_config(config)
497
498        self.assertEqual(
499            conf["default"]["OPTIONS"]["connect_timeout"],
500            "10",
501        )
502        self.assertNotIn("connect_timeout", conf["replica_0"]["OPTIONS"])
503
504    def test_db_conn_options_read_replicas(self):
505        config = ConfigLoader()
506        config.set(
507            "postgresql.replica_conn_options",
508            base64.b64encode(
509                dumps(
510                    {
511                        "connect_timeout": "10",
512                    }
513                ).encode()
514            ).decode(),
515        )
516        config.set("postgresql.read_replicas.0.host", "bar")
517        config.set("postgresql.read_replicas.1.host", "bar")
518        config.set(
519            "postgresql.read_replicas.1.conn_options",
520            base64.b64encode(
521                dumps(
522                    {
523                        "connect_timeout": "20",
524                    }
525                ).encode()
526            ).decode(),
527        )
528
529        conf = django_db_config(config)
530
531        self.assertNotIn("connect_timeout", conf["default"]["OPTIONS"])
532        self.assertEqual(
533            conf["replica_0"]["OPTIONS"]["connect_timeout"],
534            "10",
535        )
536        self.assertEqual(
537            conf["replica_1"]["OPTIONS"]["connect_timeout"],
538            "20",
539        )
540
541    # FIXME: Temporarily force pool to be deactivated.
542    # See https://github.com/goauthentik/authentik/issues/14320
543    # def test_db_pool(self):
544    #     """Test DB Config with pool"""
545    #     config = ConfigLoader()
546    #     config.set("postgresql.host", "foo")
547    #     config.set("postgresql.name", "foo")
548    #     config.set("postgresql.user", "foo")
549    #     config.set("postgresql.password", "foo")
550    #     config.set("postgresql.port", "foo")
551    #     config.set("postgresql.test.name", "foo")
552    #     config.set("postgresql.use_pool", True)
553    #     conf = django_db_config(config)
554    #     self.assertEqual(
555    #         conf,
556    #         {
557    #             "default": {
558    #                 "ENGINE": "psqlextra.backend",
559    #                 "HOST": "foo",
560    #                 "NAME": "foo",
561    #                 "OPTIONS": {
562    #                     "pool": True,
563    #                     "sslcert": None,
564    #                     "sslkey": None,
565    #                     "sslmode": None,
566    #                     "sslrootcert": None,
567    #                 },
568    #                 "PASSWORD": "foo",
569    #                 "PORT": "foo",
570    #                 "TEST": {"NAME": "foo"},
571    #                 "USER": "foo",
572    #                 "CONN_MAX_AGE": 0,
573    #                 "CONN_HEALTH_CHECKS": False,
574    #                 "DISABLE_SERVER_SIDE_CURSORS": False,
575    #             }
576    #         },
577    #     )
578
579    # def test_db_pool_options(self):
580    #     """Test DB Config with pool"""
581    #     config = ConfigLoader()
582    #     config.set("postgresql.host", "foo")
583    #     config.set("postgresql.name", "foo")
584    #     config.set("postgresql.user", "foo")
585    #     config.set("postgresql.password", "foo")
586    #     config.set("postgresql.port", "foo")
587    #     config.set("postgresql.test.name", "foo")
588    #     config.set("postgresql.use_pool", True)
589    #     config.set(
590    #         "postgresql.pool_options",
591    #         base64.b64encode(
592    #             dumps(
593    #                 {
594    #                     "max_size": 15,
595    #                 }
596    #             ).encode()
597    #         ).decode(),
598    #     )
599    #     conf = django_db_config(config)
600    #     self.assertEqual(
601    #         conf,
602    #         {
603    #             "default": {
604    #                 "ENGINE": "psqlextra.backend",
605    #                 "HOST": "foo",
606    #                 "NAME": "foo",
607    #                 "OPTIONS": {
608    #                     "pool": {
609    #                         "max_size": 15,
610    #                     },
611    #                     "sslcert": None,
612    #                     "sslkey": None,
613    #                     "sslmode": None,
614    #                     "sslrootcert": None,
615    #                 },
616    #                 "PASSWORD": "foo",
617    #                 "PORT": "foo",
618    #                 "TEST": {"NAME": "foo"},
619    #                 "USER": "foo",
620    #                 "CONN_MAX_AGE": 0,
621    #                 "CONN_HEALTH_CHECKS": False,
622    #                 "DISABLE_SERVER_SIDE_CURSORS": False,
623    #             }
624    #         },
625    #     )
class TestConfig(django.test.testcases.TestCase):
 23class TestConfig(TestCase):
 24    """Test config loader"""
 25
 26    check_deprecations_env_vars = {
 27        ENV_PREFIX + "_WORKER__CONCURRENCY": "2",
 28    }
 29
 30    @mock.patch.dict(environ, {ENV_PREFIX + "_test__test": "bar"})
 31    def test_env(self):
 32        """Test simple instance"""
 33        config = ConfigLoader()
 34        config.update_from_env()
 35        self.assertEqual(config.get("test.test"), "bar")
 36
 37    def test_patch(self):
 38        """Test patch decorator"""
 39        config = ConfigLoader()
 40        config.set("foo.bar", "bar")
 41        self.assertEqual(config.get("foo.bar"), "bar")
 42        with config.patch("foo.bar", "baz"):
 43            self.assertEqual(config.get("foo.bar"), "baz")
 44        self.assertEqual(config.get("foo.bar"), "bar")
 45
 46    @mock.patch.dict(environ, {"foo": "bar"})
 47    def test_uri_env(self):
 48        """Test URI parsing (environment)"""
 49        config = ConfigLoader()
 50        foo_uri = "env://foo"
 51        foo_parsed = config.parse_uri(foo_uri)
 52        self.assertEqual(foo_parsed.value, "bar")
 53        self.assertEqual(foo_parsed.source_type, Attr.Source.URI)
 54        self.assertEqual(foo_parsed.source, foo_uri)
 55        foo_bar_uri = "env://foo?bar"
 56        foo_bar_parsed = config.parse_uri(foo_bar_uri)
 57        self.assertEqual(foo_bar_parsed.value, "bar")
 58        self.assertEqual(foo_bar_parsed.source_type, Attr.Source.URI)
 59        self.assertEqual(foo_bar_parsed.source, foo_bar_uri)
 60
 61    def test_uri_file(self):
 62        """Test URI parsing (file load)"""
 63        config = ConfigLoader()
 64        file, file_name = mkstemp()
 65        write(file, b"foo")
 66        _, file2_name = mkstemp()
 67        chmod(file2_name, 0o000)  # Remove all permissions so we can't read the file
 68        self.assertEqual(config.parse_uri(f"file://{file_name}").value, "foo")
 69        self.assertEqual(config.parse_uri(f"file://{file2_name}?def").value, "def")
 70        unlink(file_name)
 71        unlink(file2_name)
 72
 73    def test_uri_file_update(self):
 74        """Test URI parsing (file load and update)"""
 75        file, file_name = mkstemp()
 76        write(file, b"foo")
 77        config = ConfigLoader(file_test=f"file://{file_name}")
 78        self.assertEqual(config.get("file_test"), "foo")
 79
 80        # Update config file
 81        write(file, b"bar")
 82        config.refresh("file_test")
 83        self.assertEqual(config.get("file_test"), "foobar")
 84
 85        unlink(file_name)
 86
 87    def test_uri_env_full(self):
 88        """Test URI set as env variable"""
 89        environ["AUTHENTIK_TEST_VAR"] = "file:///foo?bar"
 90        config = ConfigLoader()
 91        self.assertEqual(config.get("test_var"), "bar")
 92
 93    def test_file_update(self):
 94        """Test update_from_file"""
 95        config = ConfigLoader()
 96        file, file_name = mkstemp()
 97        write(file, b"{")
 98        file2, file2_name = mkstemp()
 99        write(file2, b"{")
100        chmod(file2_name, 0o000)  # Remove all permissions so we can't read the file
101        with self.assertRaises(ImproperlyConfigured):
102            config.update_from_file(file_name)
103        config.update_from_file(file2_name)
104        unlink(file_name)
105        unlink(file2_name)
106
107    def test_get_int(self):
108        """Test get_int"""
109        config = ConfigLoader()
110        config.set("foo", 1234)
111        self.assertEqual(config.get_int("foo"), 1234)
112
113    def test_get_int_invalid(self):
114        """Test get_int"""
115        config = ConfigLoader()
116        config.set("foo", "bar")
117        self.assertEqual(config.get_int("foo", 1234), 1234)
118
119    def test_get_dict_from_b64_json(self):
120        """Test get_dict_from_b64_json"""
121        config = ConfigLoader()
122        test_value = b'  { "foo": "bar"   }   '
123        b64_value = base64.b64encode(test_value)
124        config.set("foo", b64_value)
125        self.assertEqual(config.get_dict_from_b64_json("foo"), {"foo": "bar"})
126
127    def test_get_dict_from_b64_json_missing_brackets(self):
128        """Test get_dict_from_b64_json with missing brackets"""
129        config = ConfigLoader()
130        test_value = b' "foo": "bar"     '
131        b64_value = base64.b64encode(test_value)
132        config.set("foo", b64_value)
133        self.assertEqual(config.get_dict_from_b64_json("foo"), {})
134
135    def test_get_dict_from_b64_json_invalid(self):
136        """Test get_dict_from_b64_json with invalid value"""
137        config = ConfigLoader()
138        config.set("foo", "bar")
139        self.assertEqual(config.get_dict_from_b64_json("foo"), {})
140
141    def test_attr_json_encoder(self):
142        """Test AttrEncoder"""
143        test_attr = Attr("foo", Attr.Source.ENV, "AUTHENTIK_POSTGRESQL__USERNAME")
144        json_attr = dumps(test_attr, indent=4, cls=AttrEncoder)
145        self.assertEqual(json_attr, '"foo"')
146
147    def test_attr_json_encoder_no_attr(self):
148        """Test AttrEncoder if no Attr is passed"""
149
150        class Test:
151            """Non Attr class"""
152
153        with self.assertRaises(TypeError):
154            test_obj = Test()
155            dumps(test_obj, indent=4, cls=AttrEncoder)
156
157    def test_get_optional_int(self):
158        config = ConfigLoader()
159        self.assertEqual(config.get_optional_int("foo", 21), 21)
160        self.assertEqual(config.get_optional_int("foo"), None)
161        config.set("foo", "21")
162        self.assertEqual(config.get_optional_int("foo"), 21)
163        self.assertEqual(config.get_optional_int("foo", 0), 21)
164        self.assertEqual(config.get_optional_int("foo", "null"), 21)
165        config.set("foo", "null")
166        self.assertEqual(config.get_optional_int("foo"), None)
167        self.assertEqual(config.get_optional_int("foo", 21), None)
168
169    @mock.patch.dict(environ, check_deprecations_env_vars)
170    def test_check_deprecations(self):
171        """Test config key re-write for deprecated env vars"""
172        config = ConfigLoader()
173        config.update_from_env()
174        config.check_deprecations()
175        self.assertEqual(config.get("worker.concurrency", UNSET), UNSET)
176        self.assertEqual(config.get("worker.threads"), 2)
177
178    def test_get_keys(self):
179        """Test get_keys"""
180        config = ConfigLoader()
181        config.set("foo.bar", "baz")
182        self.assertEqual(list(config.get_keys("foo")), ["bar"])
183
184    def test_db_default(self):
185        """Test default DB Config"""
186        config = ConfigLoader()
187        config.set("postgresql.host", "foo")
188        config.set("postgresql.name", "foo")
189        config.set("postgresql.user", "foo")
190        config.set("postgresql.password", "foo")
191        config.set("postgresql.port", "foo")
192        config.set("postgresql.sslmode", "foo")
193        config.set("postgresql.sslrootcert", "foo")
194        config.set("postgresql.sslcert", "foo")
195        config.set("postgresql.sslkey", "foo")
196        config.set("postgresql.test.name", "foo")
197        conf = django_db_config(config)
198        self.assertEqual(
199            conf,
200            {
201                "default": {
202                    "ENGINE": "psqlextra.backend",
203                    "HOST": "foo",
204                    "NAME": "foo",
205                    "OPTIONS": {
206                        "pool": False,
207                        "sslcert": "foo",
208                        "sslkey": "foo",
209                        "sslmode": "foo",
210                        "sslrootcert": "foo",
211                    },
212                    "PASSWORD": "foo",
213                    "PORT": "foo",
214                    "TEST": {"NAME": "foo"},
215                    "USER": "foo",
216                    "CONN_MAX_AGE": 0,
217                    "CONN_HEALTH_CHECKS": False,
218                    "DISABLE_SERVER_SIDE_CURSORS": False,
219                }
220            },
221        )
222
223    def test_db_conn_max_age(self):
224        """Test DB conn_max_age Config"""
225        config = ConfigLoader()
226        config.set("postgresql.conn_max_age", "null")
227        conf = django_db_config(config)
228        self.assertEqual(
229            conf["default"]["CONN_MAX_AGE"],
230            None,
231        )
232
233    def test_db_read_replicas(self):
234        """Test read replicas"""
235        config = ConfigLoader()
236        config.set("postgresql.host", "foo")
237        config.set("postgresql.name", "foo")
238        config.set("postgresql.user", "foo")
239        config.set("postgresql.password", "foo")
240        config.set("postgresql.port", "foo")
241        config.set("postgresql.sslmode", "foo")
242        config.set("postgresql.sslrootcert", "foo")
243        config.set("postgresql.sslcert", "foo")
244        config.set("postgresql.sslkey", "foo")
245        config.set("postgresql.test.name", "foo")
246        # Read replica
247        config.set("postgresql.read_replicas.0.host", "bar")
248        conf = django_db_config(config)
249        self.assertEqual(
250            conf,
251            {
252                "default": {
253                    "ENGINE": "psqlextra.backend",
254                    "HOST": "foo",
255                    "NAME": "foo",
256                    "OPTIONS": {
257                        "pool": False,
258                        "sslcert": "foo",
259                        "sslkey": "foo",
260                        "sslmode": "foo",
261                        "sslrootcert": "foo",
262                    },
263                    "PASSWORD": "foo",
264                    "PORT": "foo",
265                    "TEST": {"NAME": "foo"},
266                    "USER": "foo",
267                    "CONN_MAX_AGE": 0,
268                    "CONN_HEALTH_CHECKS": False,
269                    "DISABLE_SERVER_SIDE_CURSORS": False,
270                },
271                "replica_0": {
272                    "ENGINE": "psqlextra.backend",
273                    "HOST": "bar",
274                    "NAME": "foo",
275                    "OPTIONS": {
276                        "pool": False,
277                        "sslcert": "foo",
278                        "sslkey": "foo",
279                        "sslmode": "foo",
280                        "sslrootcert": "foo",
281                    },
282                    "PASSWORD": "foo",
283                    "PORT": "foo",
284                    "TEST": {"NAME": "foo"},
285                    "USER": "foo",
286                    "CONN_MAX_AGE": 0,
287                    "CONN_HEALTH_CHECKS": False,
288                    "DISABLE_SERVER_SIDE_CURSORS": False,
289                },
290            },
291        )
292
293    def test_db_read_replicas_pgbouncer(self):
294        """Test read replicas"""
295        config = ConfigLoader()
296        config.set("postgresql.host", "foo")
297        config.set("postgresql.name", "foo")
298        config.set("postgresql.user", "foo")
299        config.set("postgresql.password", "foo")
300        config.set("postgresql.port", "foo")
301        config.set("postgresql.sslmode", "foo")
302        config.set("postgresql.sslrootcert", "foo")
303        config.set("postgresql.sslcert", "foo")
304        config.set("postgresql.sslkey", "foo")
305        config.set("postgresql.test.name", "foo")
306        config.set("postgresql.use_pgbouncer", True)
307        # Read replica
308        config.set("postgresql.read_replicas.0.host", "bar")
309        # Override conn_max_age
310        config.set("postgresql.read_replicas.0.conn_max_age", 10)
311        # This isn't supported
312        config.set("postgresql.read_replicas.0.use_pgbouncer", False)
313        conf = django_db_config(config)
314        self.assertEqual(
315            conf,
316            {
317                "default": {
318                    "DISABLE_SERVER_SIDE_CURSORS": True,
319                    "CONN_MAX_AGE": None,
320                    "CONN_HEALTH_CHECKS": False,
321                    "ENGINE": "psqlextra.backend",
322                    "HOST": "foo",
323                    "NAME": "foo",
324                    "OPTIONS": {
325                        "pool": False,
326                        "sslcert": "foo",
327                        "sslkey": "foo",
328                        "sslmode": "foo",
329                        "sslrootcert": "foo",
330                    },
331                    "PASSWORD": "foo",
332                    "PORT": "foo",
333                    "TEST": {"NAME": "foo"},
334                    "USER": "foo",
335                },
336                "replica_0": {
337                    "DISABLE_SERVER_SIDE_CURSORS": True,
338                    "CONN_MAX_AGE": 10,
339                    "CONN_HEALTH_CHECKS": False,
340                    "ENGINE": "psqlextra.backend",
341                    "HOST": "bar",
342                    "NAME": "foo",
343                    "OPTIONS": {
344                        "pool": False,
345                        "sslcert": "foo",
346                        "sslkey": "foo",
347                        "sslmode": "foo",
348                        "sslrootcert": "foo",
349                    },
350                    "PASSWORD": "foo",
351                    "PORT": "foo",
352                    "TEST": {"NAME": "foo"},
353                    "USER": "foo",
354                },
355            },
356        )
357
358    def test_db_read_replicas_pgpool(self):
359        """Test read replicas"""
360        config = ConfigLoader()
361        config.set("postgresql.host", "foo")
362        config.set("postgresql.name", "foo")
363        config.set("postgresql.user", "foo")
364        config.set("postgresql.password", "foo")
365        config.set("postgresql.port", "foo")
366        config.set("postgresql.sslmode", "foo")
367        config.set("postgresql.sslrootcert", "foo")
368        config.set("postgresql.sslcert", "foo")
369        config.set("postgresql.sslkey", "foo")
370        config.set("postgresql.test.name", "foo")
371        config.set("postgresql.use_pgpool", True)
372        # Read replica
373        config.set("postgresql.read_replicas.0.host", "bar")
374        # This isn't supported
375        config.set("postgresql.read_replicas.0.use_pgpool", False)
376        conf = django_db_config(config)
377        self.assertEqual(
378            conf,
379            {
380                "default": {
381                    "DISABLE_SERVER_SIDE_CURSORS": True,
382                    "CONN_MAX_AGE": 0,
383                    "CONN_HEALTH_CHECKS": False,
384                    "ENGINE": "psqlextra.backend",
385                    "HOST": "foo",
386                    "NAME": "foo",
387                    "OPTIONS": {
388                        "pool": False,
389                        "sslcert": "foo",
390                        "sslkey": "foo",
391                        "sslmode": "foo",
392                        "sslrootcert": "foo",
393                    },
394                    "PASSWORD": "foo",
395                    "PORT": "foo",
396                    "TEST": {"NAME": "foo"},
397                    "USER": "foo",
398                },
399                "replica_0": {
400                    "DISABLE_SERVER_SIDE_CURSORS": True,
401                    "CONN_MAX_AGE": 0,
402                    "CONN_HEALTH_CHECKS": False,
403                    "ENGINE": "psqlextra.backend",
404                    "HOST": "bar",
405                    "NAME": "foo",
406                    "OPTIONS": {
407                        "pool": False,
408                        "sslcert": "foo",
409                        "sslkey": "foo",
410                        "sslmode": "foo",
411                        "sslrootcert": "foo",
412                    },
413                    "PASSWORD": "foo",
414                    "PORT": "foo",
415                    "TEST": {"NAME": "foo"},
416                    "USER": "foo",
417                },
418            },
419        )
420
421    def test_db_read_replicas_diff_ssl(self):
422        """Test read replicas (with different SSL Settings)"""
423        """Test read replicas"""
424        config = ConfigLoader()
425        config.set("postgresql.host", "foo")
426        config.set("postgresql.name", "foo")
427        config.set("postgresql.user", "foo")
428        config.set("postgresql.password", "foo")
429        config.set("postgresql.port", "foo")
430        config.set("postgresql.sslmode", "foo")
431        config.set("postgresql.sslrootcert", "foo")
432        config.set("postgresql.sslcert", "foo")
433        config.set("postgresql.sslkey", "foo")
434        config.set("postgresql.test.name", "foo")
435        # Read replica
436        config.set("postgresql.read_replicas.0.host", "bar")
437        config.set("postgresql.read_replicas.0.sslcert", "bar")
438        conf = django_db_config(config)
439        self.assertEqual(
440            conf,
441            {
442                "default": {
443                    "ENGINE": "psqlextra.backend",
444                    "HOST": "foo",
445                    "NAME": "foo",
446                    "OPTIONS": {
447                        "pool": False,
448                        "sslcert": "foo",
449                        "sslkey": "foo",
450                        "sslmode": "foo",
451                        "sslrootcert": "foo",
452                    },
453                    "PASSWORD": "foo",
454                    "PORT": "foo",
455                    "TEST": {"NAME": "foo"},
456                    "USER": "foo",
457                    "DISABLE_SERVER_SIDE_CURSORS": False,
458                    "CONN_MAX_AGE": 0,
459                    "CONN_HEALTH_CHECKS": False,
460                },
461                "replica_0": {
462                    "ENGINE": "psqlextra.backend",
463                    "HOST": "bar",
464                    "NAME": "foo",
465                    "OPTIONS": {
466                        "pool": False,
467                        "sslcert": "bar",
468                        "sslkey": "foo",
469                        "sslmode": "foo",
470                        "sslrootcert": "foo",
471                    },
472                    "PASSWORD": "foo",
473                    "PORT": "foo",
474                    "TEST": {"NAME": "foo"},
475                    "USER": "foo",
476                    "DISABLE_SERVER_SIDE_CURSORS": False,
477                    "CONN_MAX_AGE": 0,
478                    "CONN_HEALTH_CHECKS": False,
479                },
480            },
481        )
482
483    def test_db_conn_options(self):
484        config = ConfigLoader()
485        config.set(
486            "postgresql.conn_options",
487            base64.b64encode(
488                dumps(
489                    {
490                        "connect_timeout": "10",
491                    }
492                ).encode()
493            ).decode(),
494        )
495        config.set("postgresql.read_replicas.0.host", "bar")
496
497        conf = django_db_config(config)
498
499        self.assertEqual(
500            conf["default"]["OPTIONS"]["connect_timeout"],
501            "10",
502        )
503        self.assertNotIn("connect_timeout", conf["replica_0"]["OPTIONS"])
504
505    def test_db_conn_options_read_replicas(self):
506        config = ConfigLoader()
507        config.set(
508            "postgresql.replica_conn_options",
509            base64.b64encode(
510                dumps(
511                    {
512                        "connect_timeout": "10",
513                    }
514                ).encode()
515            ).decode(),
516        )
517        config.set("postgresql.read_replicas.0.host", "bar")
518        config.set("postgresql.read_replicas.1.host", "bar")
519        config.set(
520            "postgresql.read_replicas.1.conn_options",
521            base64.b64encode(
522                dumps(
523                    {
524                        "connect_timeout": "20",
525                    }
526                ).encode()
527            ).decode(),
528        )
529
530        conf = django_db_config(config)
531
532        self.assertNotIn("connect_timeout", conf["default"]["OPTIONS"])
533        self.assertEqual(
534            conf["replica_0"]["OPTIONS"]["connect_timeout"],
535            "10",
536        )
537        self.assertEqual(
538            conf["replica_1"]["OPTIONS"]["connect_timeout"],
539            "20",
540        )
541
542    # FIXME: Temporarily force pool to be deactivated.
543    # See https://github.com/goauthentik/authentik/issues/14320
544    # def test_db_pool(self):
545    #     """Test DB Config with pool"""
546    #     config = ConfigLoader()
547    #     config.set("postgresql.host", "foo")
548    #     config.set("postgresql.name", "foo")
549    #     config.set("postgresql.user", "foo")
550    #     config.set("postgresql.password", "foo")
551    #     config.set("postgresql.port", "foo")
552    #     config.set("postgresql.test.name", "foo")
553    #     config.set("postgresql.use_pool", True)
554    #     conf = django_db_config(config)
555    #     self.assertEqual(
556    #         conf,
557    #         {
558    #             "default": {
559    #                 "ENGINE": "psqlextra.backend",
560    #                 "HOST": "foo",
561    #                 "NAME": "foo",
562    #                 "OPTIONS": {
563    #                     "pool": True,
564    #                     "sslcert": None,
565    #                     "sslkey": None,
566    #                     "sslmode": None,
567    #                     "sslrootcert": None,
568    #                 },
569    #                 "PASSWORD": "foo",
570    #                 "PORT": "foo",
571    #                 "TEST": {"NAME": "foo"},
572    #                 "USER": "foo",
573    #                 "CONN_MAX_AGE": 0,
574    #                 "CONN_HEALTH_CHECKS": False,
575    #                 "DISABLE_SERVER_SIDE_CURSORS": False,
576    #             }
577    #         },
578    #     )
579
580    # def test_db_pool_options(self):
581    #     """Test DB Config with pool"""
582    #     config = ConfigLoader()
583    #     config.set("postgresql.host", "foo")
584    #     config.set("postgresql.name", "foo")
585    #     config.set("postgresql.user", "foo")
586    #     config.set("postgresql.password", "foo")
587    #     config.set("postgresql.port", "foo")
588    #     config.set("postgresql.test.name", "foo")
589    #     config.set("postgresql.use_pool", True)
590    #     config.set(
591    #         "postgresql.pool_options",
592    #         base64.b64encode(
593    #             dumps(
594    #                 {
595    #                     "max_size": 15,
596    #                 }
597    #             ).encode()
598    #         ).decode(),
599    #     )
600    #     conf = django_db_config(config)
601    #     self.assertEqual(
602    #         conf,
603    #         {
604    #             "default": {
605    #                 "ENGINE": "psqlextra.backend",
606    #                 "HOST": "foo",
607    #                 "NAME": "foo",
608    #                 "OPTIONS": {
609    #                     "pool": {
610    #                         "max_size": 15,
611    #                     },
612    #                     "sslcert": None,
613    #                     "sslkey": None,
614    #                     "sslmode": None,
615    #                     "sslrootcert": None,
616    #                 },
617    #                 "PASSWORD": "foo",
618    #                 "PORT": "foo",
619    #                 "TEST": {"NAME": "foo"},
620    #                 "USER": "foo",
621    #                 "CONN_MAX_AGE": 0,
622    #                 "CONN_HEALTH_CHECKS": False,
623    #                 "DISABLE_SERVER_SIDE_CURSORS": False,
624    #             }
625    #         },
626    #     )

Test config loader

check_deprecations_env_vars = {'AUTHENTIK_WORKER__CONCURRENCY': '2'}
@mock.patch.dict(environ, {ENV_PREFIX + '_test__test': 'bar'})
def test_env(self):
30    @mock.patch.dict(environ, {ENV_PREFIX + "_test__test": "bar"})
31    def test_env(self):
32        """Test simple instance"""
33        config = ConfigLoader()
34        config.update_from_env()
35        self.assertEqual(config.get("test.test"), "bar")

Test simple instance

def test_patch(self):
37    def test_patch(self):
38        """Test patch decorator"""
39        config = ConfigLoader()
40        config.set("foo.bar", "bar")
41        self.assertEqual(config.get("foo.bar"), "bar")
42        with config.patch("foo.bar", "baz"):
43            self.assertEqual(config.get("foo.bar"), "baz")
44        self.assertEqual(config.get("foo.bar"), "bar")

Test patch decorator

@mock.patch.dict(environ, {'foo': 'bar'})
def test_uri_env(self):
46    @mock.patch.dict(environ, {"foo": "bar"})
47    def test_uri_env(self):
48        """Test URI parsing (environment)"""
49        config = ConfigLoader()
50        foo_uri = "env://foo"
51        foo_parsed = config.parse_uri(foo_uri)
52        self.assertEqual(foo_parsed.value, "bar")
53        self.assertEqual(foo_parsed.source_type, Attr.Source.URI)
54        self.assertEqual(foo_parsed.source, foo_uri)
55        foo_bar_uri = "env://foo?bar"
56        foo_bar_parsed = config.parse_uri(foo_bar_uri)
57        self.assertEqual(foo_bar_parsed.value, "bar")
58        self.assertEqual(foo_bar_parsed.source_type, Attr.Source.URI)
59        self.assertEqual(foo_bar_parsed.source, foo_bar_uri)

Test URI parsing (environment)

def test_uri_file(self):
61    def test_uri_file(self):
62        """Test URI parsing (file load)"""
63        config = ConfigLoader()
64        file, file_name = mkstemp()
65        write(file, b"foo")
66        _, file2_name = mkstemp()
67        chmod(file2_name, 0o000)  # Remove all permissions so we can't read the file
68        self.assertEqual(config.parse_uri(f"file://{file_name}").value, "foo")
69        self.assertEqual(config.parse_uri(f"file://{file2_name}?def").value, "def")
70        unlink(file_name)
71        unlink(file2_name)

Test URI parsing (file load)

def test_uri_file_update(self):
73    def test_uri_file_update(self):
74        """Test URI parsing (file load and update)"""
75        file, file_name = mkstemp()
76        write(file, b"foo")
77        config = ConfigLoader(file_test=f"file://{file_name}")
78        self.assertEqual(config.get("file_test"), "foo")
79
80        # Update config file
81        write(file, b"bar")
82        config.refresh("file_test")
83        self.assertEqual(config.get("file_test"), "foobar")
84
85        unlink(file_name)

Test URI parsing (file load and update)

def test_uri_env_full(self):
87    def test_uri_env_full(self):
88        """Test URI set as env variable"""
89        environ["AUTHENTIK_TEST_VAR"] = "file:///foo?bar"
90        config = ConfigLoader()
91        self.assertEqual(config.get("test_var"), "bar")

Test URI set as env variable

def test_file_update(self):
 93    def test_file_update(self):
 94        """Test update_from_file"""
 95        config = ConfigLoader()
 96        file, file_name = mkstemp()
 97        write(file, b"{")
 98        file2, file2_name = mkstemp()
 99        write(file2, b"{")
100        chmod(file2_name, 0o000)  # Remove all permissions so we can't read the file
101        with self.assertRaises(ImproperlyConfigured):
102            config.update_from_file(file_name)
103        config.update_from_file(file2_name)
104        unlink(file_name)
105        unlink(file2_name)

Test update_from_file

def test_get_int(self):
107    def test_get_int(self):
108        """Test get_int"""
109        config = ConfigLoader()
110        config.set("foo", 1234)
111        self.assertEqual(config.get_int("foo"), 1234)

Test get_int

def test_get_int_invalid(self):
113    def test_get_int_invalid(self):
114        """Test get_int"""
115        config = ConfigLoader()
116        config.set("foo", "bar")
117        self.assertEqual(config.get_int("foo", 1234), 1234)

Test get_int

def test_get_dict_from_b64_json(self):
119    def test_get_dict_from_b64_json(self):
120        """Test get_dict_from_b64_json"""
121        config = ConfigLoader()
122        test_value = b'  { "foo": "bar"   }   '
123        b64_value = base64.b64encode(test_value)
124        config.set("foo", b64_value)
125        self.assertEqual(config.get_dict_from_b64_json("foo"), {"foo": "bar"})

Test get_dict_from_b64_json

def test_get_dict_from_b64_json_missing_brackets(self):
127    def test_get_dict_from_b64_json_missing_brackets(self):
128        """Test get_dict_from_b64_json with missing brackets"""
129        config = ConfigLoader()
130        test_value = b' "foo": "bar"     '
131        b64_value = base64.b64encode(test_value)
132        config.set("foo", b64_value)
133        self.assertEqual(config.get_dict_from_b64_json("foo"), {})

Test get_dict_from_b64_json with missing brackets

def test_get_dict_from_b64_json_invalid(self):
135    def test_get_dict_from_b64_json_invalid(self):
136        """Test get_dict_from_b64_json with invalid value"""
137        config = ConfigLoader()
138        config.set("foo", "bar")
139        self.assertEqual(config.get_dict_from_b64_json("foo"), {})

Test get_dict_from_b64_json with invalid value

def test_attr_json_encoder(self):
141    def test_attr_json_encoder(self):
142        """Test AttrEncoder"""
143        test_attr = Attr("foo", Attr.Source.ENV, "AUTHENTIK_POSTGRESQL__USERNAME")
144        json_attr = dumps(test_attr, indent=4, cls=AttrEncoder)
145        self.assertEqual(json_attr, '"foo"')

Test AttrEncoder

def test_attr_json_encoder_no_attr(self):
147    def test_attr_json_encoder_no_attr(self):
148        """Test AttrEncoder if no Attr is passed"""
149
150        class Test:
151            """Non Attr class"""
152
153        with self.assertRaises(TypeError):
154            test_obj = Test()
155            dumps(test_obj, indent=4, cls=AttrEncoder)

Test AttrEncoder if no Attr is passed

def test_get_optional_int(self):
157    def test_get_optional_int(self):
158        config = ConfigLoader()
159        self.assertEqual(config.get_optional_int("foo", 21), 21)
160        self.assertEqual(config.get_optional_int("foo"), None)
161        config.set("foo", "21")
162        self.assertEqual(config.get_optional_int("foo"), 21)
163        self.assertEqual(config.get_optional_int("foo", 0), 21)
164        self.assertEqual(config.get_optional_int("foo", "null"), 21)
165        config.set("foo", "null")
166        self.assertEqual(config.get_optional_int("foo"), None)
167        self.assertEqual(config.get_optional_int("foo", 21), None)
@mock.patch.dict(environ, check_deprecations_env_vars)
def test_check_deprecations(self):
169    @mock.patch.dict(environ, check_deprecations_env_vars)
170    def test_check_deprecations(self):
171        """Test config key re-write for deprecated env vars"""
172        config = ConfigLoader()
173        config.update_from_env()
174        config.check_deprecations()
175        self.assertEqual(config.get("worker.concurrency", UNSET), UNSET)
176        self.assertEqual(config.get("worker.threads"), 2)

Test config key re-write for deprecated env vars

def test_get_keys(self):
178    def test_get_keys(self):
179        """Test get_keys"""
180        config = ConfigLoader()
181        config.set("foo.bar", "baz")
182        self.assertEqual(list(config.get_keys("foo")), ["bar"])

Test get_keys

def test_db_default(self):
184    def test_db_default(self):
185        """Test default DB Config"""
186        config = ConfigLoader()
187        config.set("postgresql.host", "foo")
188        config.set("postgresql.name", "foo")
189        config.set("postgresql.user", "foo")
190        config.set("postgresql.password", "foo")
191        config.set("postgresql.port", "foo")
192        config.set("postgresql.sslmode", "foo")
193        config.set("postgresql.sslrootcert", "foo")
194        config.set("postgresql.sslcert", "foo")
195        config.set("postgresql.sslkey", "foo")
196        config.set("postgresql.test.name", "foo")
197        conf = django_db_config(config)
198        self.assertEqual(
199            conf,
200            {
201                "default": {
202                    "ENGINE": "psqlextra.backend",
203                    "HOST": "foo",
204                    "NAME": "foo",
205                    "OPTIONS": {
206                        "pool": False,
207                        "sslcert": "foo",
208                        "sslkey": "foo",
209                        "sslmode": "foo",
210                        "sslrootcert": "foo",
211                    },
212                    "PASSWORD": "foo",
213                    "PORT": "foo",
214                    "TEST": {"NAME": "foo"},
215                    "USER": "foo",
216                    "CONN_MAX_AGE": 0,
217                    "CONN_HEALTH_CHECKS": False,
218                    "DISABLE_SERVER_SIDE_CURSORS": False,
219                }
220            },
221        )

Test default DB Config

def test_db_conn_max_age(self):
223    def test_db_conn_max_age(self):
224        """Test DB conn_max_age Config"""
225        config = ConfigLoader()
226        config.set("postgresql.conn_max_age", "null")
227        conf = django_db_config(config)
228        self.assertEqual(
229            conf["default"]["CONN_MAX_AGE"],
230            None,
231        )

Test DB conn_max_age Config

def test_db_read_replicas(self):
233    def test_db_read_replicas(self):
234        """Test read replicas"""
235        config = ConfigLoader()
236        config.set("postgresql.host", "foo")
237        config.set("postgresql.name", "foo")
238        config.set("postgresql.user", "foo")
239        config.set("postgresql.password", "foo")
240        config.set("postgresql.port", "foo")
241        config.set("postgresql.sslmode", "foo")
242        config.set("postgresql.sslrootcert", "foo")
243        config.set("postgresql.sslcert", "foo")
244        config.set("postgresql.sslkey", "foo")
245        config.set("postgresql.test.name", "foo")
246        # Read replica
247        config.set("postgresql.read_replicas.0.host", "bar")
248        conf = django_db_config(config)
249        self.assertEqual(
250            conf,
251            {
252                "default": {
253                    "ENGINE": "psqlextra.backend",
254                    "HOST": "foo",
255                    "NAME": "foo",
256                    "OPTIONS": {
257                        "pool": False,
258                        "sslcert": "foo",
259                        "sslkey": "foo",
260                        "sslmode": "foo",
261                        "sslrootcert": "foo",
262                    },
263                    "PASSWORD": "foo",
264                    "PORT": "foo",
265                    "TEST": {"NAME": "foo"},
266                    "USER": "foo",
267                    "CONN_MAX_AGE": 0,
268                    "CONN_HEALTH_CHECKS": False,
269                    "DISABLE_SERVER_SIDE_CURSORS": False,
270                },
271                "replica_0": {
272                    "ENGINE": "psqlextra.backend",
273                    "HOST": "bar",
274                    "NAME": "foo",
275                    "OPTIONS": {
276                        "pool": False,
277                        "sslcert": "foo",
278                        "sslkey": "foo",
279                        "sslmode": "foo",
280                        "sslrootcert": "foo",
281                    },
282                    "PASSWORD": "foo",
283                    "PORT": "foo",
284                    "TEST": {"NAME": "foo"},
285                    "USER": "foo",
286                    "CONN_MAX_AGE": 0,
287                    "CONN_HEALTH_CHECKS": False,
288                    "DISABLE_SERVER_SIDE_CURSORS": False,
289                },
290            },
291        )

Test read replicas

def test_db_read_replicas_pgbouncer(self):
293    def test_db_read_replicas_pgbouncer(self):
294        """Test read replicas"""
295        config = ConfigLoader()
296        config.set("postgresql.host", "foo")
297        config.set("postgresql.name", "foo")
298        config.set("postgresql.user", "foo")
299        config.set("postgresql.password", "foo")
300        config.set("postgresql.port", "foo")
301        config.set("postgresql.sslmode", "foo")
302        config.set("postgresql.sslrootcert", "foo")
303        config.set("postgresql.sslcert", "foo")
304        config.set("postgresql.sslkey", "foo")
305        config.set("postgresql.test.name", "foo")
306        config.set("postgresql.use_pgbouncer", True)
307        # Read replica
308        config.set("postgresql.read_replicas.0.host", "bar")
309        # Override conn_max_age
310        config.set("postgresql.read_replicas.0.conn_max_age", 10)
311        # This isn't supported
312        config.set("postgresql.read_replicas.0.use_pgbouncer", False)
313        conf = django_db_config(config)
314        self.assertEqual(
315            conf,
316            {
317                "default": {
318                    "DISABLE_SERVER_SIDE_CURSORS": True,
319                    "CONN_MAX_AGE": None,
320                    "CONN_HEALTH_CHECKS": False,
321                    "ENGINE": "psqlextra.backend",
322                    "HOST": "foo",
323                    "NAME": "foo",
324                    "OPTIONS": {
325                        "pool": False,
326                        "sslcert": "foo",
327                        "sslkey": "foo",
328                        "sslmode": "foo",
329                        "sslrootcert": "foo",
330                    },
331                    "PASSWORD": "foo",
332                    "PORT": "foo",
333                    "TEST": {"NAME": "foo"},
334                    "USER": "foo",
335                },
336                "replica_0": {
337                    "DISABLE_SERVER_SIDE_CURSORS": True,
338                    "CONN_MAX_AGE": 10,
339                    "CONN_HEALTH_CHECKS": False,
340                    "ENGINE": "psqlextra.backend",
341                    "HOST": "bar",
342                    "NAME": "foo",
343                    "OPTIONS": {
344                        "pool": False,
345                        "sslcert": "foo",
346                        "sslkey": "foo",
347                        "sslmode": "foo",
348                        "sslrootcert": "foo",
349                    },
350                    "PASSWORD": "foo",
351                    "PORT": "foo",
352                    "TEST": {"NAME": "foo"},
353                    "USER": "foo",
354                },
355            },
356        )

Test read replicas

def test_db_read_replicas_pgpool(self):
358    def test_db_read_replicas_pgpool(self):
359        """Test read replicas"""
360        config = ConfigLoader()
361        config.set("postgresql.host", "foo")
362        config.set("postgresql.name", "foo")
363        config.set("postgresql.user", "foo")
364        config.set("postgresql.password", "foo")
365        config.set("postgresql.port", "foo")
366        config.set("postgresql.sslmode", "foo")
367        config.set("postgresql.sslrootcert", "foo")
368        config.set("postgresql.sslcert", "foo")
369        config.set("postgresql.sslkey", "foo")
370        config.set("postgresql.test.name", "foo")
371        config.set("postgresql.use_pgpool", True)
372        # Read replica
373        config.set("postgresql.read_replicas.0.host", "bar")
374        # This isn't supported
375        config.set("postgresql.read_replicas.0.use_pgpool", False)
376        conf = django_db_config(config)
377        self.assertEqual(
378            conf,
379            {
380                "default": {
381                    "DISABLE_SERVER_SIDE_CURSORS": True,
382                    "CONN_MAX_AGE": 0,
383                    "CONN_HEALTH_CHECKS": False,
384                    "ENGINE": "psqlextra.backend",
385                    "HOST": "foo",
386                    "NAME": "foo",
387                    "OPTIONS": {
388                        "pool": False,
389                        "sslcert": "foo",
390                        "sslkey": "foo",
391                        "sslmode": "foo",
392                        "sslrootcert": "foo",
393                    },
394                    "PASSWORD": "foo",
395                    "PORT": "foo",
396                    "TEST": {"NAME": "foo"},
397                    "USER": "foo",
398                },
399                "replica_0": {
400                    "DISABLE_SERVER_SIDE_CURSORS": True,
401                    "CONN_MAX_AGE": 0,
402                    "CONN_HEALTH_CHECKS": False,
403                    "ENGINE": "psqlextra.backend",
404                    "HOST": "bar",
405                    "NAME": "foo",
406                    "OPTIONS": {
407                        "pool": False,
408                        "sslcert": "foo",
409                        "sslkey": "foo",
410                        "sslmode": "foo",
411                        "sslrootcert": "foo",
412                    },
413                    "PASSWORD": "foo",
414                    "PORT": "foo",
415                    "TEST": {"NAME": "foo"},
416                    "USER": "foo",
417                },
418            },
419        )

Test read replicas

def test_db_read_replicas_diff_ssl(self):
421    def test_db_read_replicas_diff_ssl(self):
422        """Test read replicas (with different SSL Settings)"""
423        """Test read replicas"""
424        config = ConfigLoader()
425        config.set("postgresql.host", "foo")
426        config.set("postgresql.name", "foo")
427        config.set("postgresql.user", "foo")
428        config.set("postgresql.password", "foo")
429        config.set("postgresql.port", "foo")
430        config.set("postgresql.sslmode", "foo")
431        config.set("postgresql.sslrootcert", "foo")
432        config.set("postgresql.sslcert", "foo")
433        config.set("postgresql.sslkey", "foo")
434        config.set("postgresql.test.name", "foo")
435        # Read replica
436        config.set("postgresql.read_replicas.0.host", "bar")
437        config.set("postgresql.read_replicas.0.sslcert", "bar")
438        conf = django_db_config(config)
439        self.assertEqual(
440            conf,
441            {
442                "default": {
443                    "ENGINE": "psqlextra.backend",
444                    "HOST": "foo",
445                    "NAME": "foo",
446                    "OPTIONS": {
447                        "pool": False,
448                        "sslcert": "foo",
449                        "sslkey": "foo",
450                        "sslmode": "foo",
451                        "sslrootcert": "foo",
452                    },
453                    "PASSWORD": "foo",
454                    "PORT": "foo",
455                    "TEST": {"NAME": "foo"},
456                    "USER": "foo",
457                    "DISABLE_SERVER_SIDE_CURSORS": False,
458                    "CONN_MAX_AGE": 0,
459                    "CONN_HEALTH_CHECKS": False,
460                },
461                "replica_0": {
462                    "ENGINE": "psqlextra.backend",
463                    "HOST": "bar",
464                    "NAME": "foo",
465                    "OPTIONS": {
466                        "pool": False,
467                        "sslcert": "bar",
468                        "sslkey": "foo",
469                        "sslmode": "foo",
470                        "sslrootcert": "foo",
471                    },
472                    "PASSWORD": "foo",
473                    "PORT": "foo",
474                    "TEST": {"NAME": "foo"},
475                    "USER": "foo",
476                    "DISABLE_SERVER_SIDE_CURSORS": False,
477                    "CONN_MAX_AGE": 0,
478                    "CONN_HEALTH_CHECKS": False,
479                },
480            },
481        )

Test read replicas (with different SSL Settings)

def test_db_conn_options(self):
483    def test_db_conn_options(self):
484        config = ConfigLoader()
485        config.set(
486            "postgresql.conn_options",
487            base64.b64encode(
488                dumps(
489                    {
490                        "connect_timeout": "10",
491                    }
492                ).encode()
493            ).decode(),
494        )
495        config.set("postgresql.read_replicas.0.host", "bar")
496
497        conf = django_db_config(config)
498
499        self.assertEqual(
500            conf["default"]["OPTIONS"]["connect_timeout"],
501            "10",
502        )
503        self.assertNotIn("connect_timeout", conf["replica_0"]["OPTIONS"])
def test_db_conn_options_read_replicas(self):
505    def test_db_conn_options_read_replicas(self):
506        config = ConfigLoader()
507        config.set(
508            "postgresql.replica_conn_options",
509            base64.b64encode(
510                dumps(
511                    {
512                        "connect_timeout": "10",
513                    }
514                ).encode()
515            ).decode(),
516        )
517        config.set("postgresql.read_replicas.0.host", "bar")
518        config.set("postgresql.read_replicas.1.host", "bar")
519        config.set(
520            "postgresql.read_replicas.1.conn_options",
521            base64.b64encode(
522                dumps(
523                    {
524                        "connect_timeout": "20",
525                    }
526                ).encode()
527            ).decode(),
528        )
529
530        conf = django_db_config(config)
531
532        self.assertNotIn("connect_timeout", conf["default"]["OPTIONS"])
533        self.assertEqual(
534            conf["replica_0"]["OPTIONS"]["connect_timeout"],
535            "10",
536        )
537        self.assertEqual(
538            conf["replica_1"]["OPTIONS"]["connect_timeout"],
539            "20",
540        )