crypticorn-utils 0.1.1rc1__py3-none-any.whl → 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
crypticorn_utils/auth.py CHANGED
@@ -61,8 +61,8 @@ class AuthHandler:
61
61
  """
62
62
  Verifies the API key.
63
63
  """
64
- # self.client.config.api_key = {apikey_header.scheme_name: api_key}
65
- return await self.client.login.verify_api_key(api_key)
64
+ self.client.config.api_key = {"APIKeyHeader": api_key}
65
+ return await self.client.login.verify()
66
66
 
67
67
  async def _verify_bearer(
68
68
  self, bearer: HTTPAuthorizationCredentials
@@ -80,7 +80,7 @@ class AuthHandler:
80
80
  return await self.client.login.verify_basic_auth(basic.username, basic.password)
81
81
 
82
82
  async def _validate_scopes(
83
- self, api_scopes: list[Scope], user_scopes: list[Scope]
83
+ self, api_scopes: list[str], user_scopes: list[str]
84
84
  ) -> bool:
85
85
  """
86
86
  Checks if the required scopes are a subset of the user scopes.
@@ -92,6 +92,10 @@ class AuthHandler:
92
92
  message="Insufficient scopes to access this resource (required: "
93
93
  + ", ".join(api_scopes)
94
94
  + ")",
95
+ details={
96
+ "required_scopes": api_scopes,
97
+ "granted_scopes": user_scopes,
98
+ },
95
99
  ),
96
100
  )
97
101
 
@@ -277,7 +281,12 @@ class AuthHandler:
277
281
  if res is None:
278
282
  continue
279
283
  if sec:
280
- await self._validate_scopes(sec.scopes, res.scopes)
284
+ required = [
285
+ s.value if isinstance(s, Scope) else str(s)
286
+ for s in sec.scopes
287
+ ]
288
+ granted = [str(s) for s in res.scopes]
289
+ await self._validate_scopes(required, granted)
281
290
  return res
282
291
 
283
292
  except Exception as e:
crypticorn_utils/enums.py CHANGED
@@ -50,8 +50,6 @@ class Scope(StrEnum):
50
50
  READ_PAY_PAYMENTS = "read:pay:payments"
51
51
  READ_PAY_PRODUCTS = "read:pay:products"
52
52
  WRITE_PAY_PRODUCTS = "write:pay:products"
53
- READ_PAY_NOW = "read:pay:now"
54
- WRITE_PAY_NOW = "write:pay:now"
55
53
  WRITE_PAY_COUPONS = "write:pay:coupons"
56
54
  READ_PAY_COUPONS = "read:pay:coupons"
57
55
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crypticorn_utils
3
- Version: 0.1.1rc1
3
+ Version: 0.1.2
4
4
  Summary: Shared utilities for the Crypticorn APIs
5
5
  Author-email: Crypticorn <timon@crypticorn.com>
6
6
  License-Expression: MIT
@@ -26,6 +26,7 @@ Requires-Dist: psutil<8.0.0,>=7.0.0
26
26
  Requires-Dist: setuptools<81.0.0,>=80.0.0
27
27
  Requires-Dist: strenum
28
28
  Requires-Dist: prometheus-client<1.0.0,>=0.22.0
29
+ Requires-Dist: crypticorn<3.0.0,>=2.17.0
29
30
  Provides-Extra: dev
30
31
  Requires-Dist: build; extra == "dev"
31
32
  Requires-Dist: twine; extra == "dev"
@@ -42,7 +43,6 @@ Requires-Dist: pytest-cov==6.1.1; extra == "test"
42
43
  Requires-Dist: python-dotenv==1.0.1; extra == "test"
43
44
  Requires-Dist: PyJWT==2.10.0; extra == "test"
44
45
  Requires-Dist: httpx>=0.27.0; extra == "test"
45
- Requires-Dist: crypticorn>=2.17.0; extra == "test"
46
46
  Dynamic: license-file
47
47
 
48
48
  This module serves as a central place for providing utilities for our python backends.
@@ -68,6 +68,30 @@ This module serves as a central place for providing utilities for our python bac
68
68
 
69
69
  <!-- changelog-insertion -->
70
70
 
71
+ ## v0.1.2 (2025-07-22)
72
+
73
+ ### Bug Fixes
74
+
75
+ - Failing scope comparison in some environments
76
+ ([`004495d`](https://github.com/crypticorn-ai/util-libraries/commit/004495da87b973f43b3e32abe96493dac1b6204a))
77
+
78
+
79
+ ## v0.1.1 (2025-07-20)
80
+
81
+
82
+ ## v0.1.1-rc.2 (2025-07-18)
83
+
84
+ ### Bug Fixes
85
+
86
+ - Change verify api key request
87
+ ([`11af6c2`](https://github.com/crypticorn-ai/util-libraries/commit/11af6c2c12a92b0a4c1c3b83ba86aca2a401ac45))
88
+
89
+ ### Testing
90
+
91
+ - Fix failing auth test
92
+ ([`03a3db1`](https://github.com/crypticorn-ai/util-libraries/commit/03a3db1341aaf51c6162454924cb929d6cfee94a))
93
+
94
+
71
95
  ## v0.1.1-rc.1 (2025-07-02)
72
96
 
73
97
  ### Bug Fixes
@@ -1,9 +1,9 @@
1
1
  crypticorn_utils/__init__.py,sha256=PlxMppwUMmbNR9Jr1z9DNCYAYnTuWzW9IFyGTwV5slA,700
2
2
  crypticorn_utils/_migration.py,sha256=YPVEDVIz9Lt3ntwrVmUtavQxsJVjtSqQeQ0A_qydiaY,445
3
3
  crypticorn_utils/ansi_colors.py,sha256=-tMlUTE8NI7TPv7uj0kGRe-SI2hGaUNPKBFI_dfiZy0,1392
4
- crypticorn_utils/auth.py,sha256=oJORAuYBD0qiAmUpbMe0JSvp_gsof3oQMNpqay8L6Ek,13018
4
+ crypticorn_utils/auth.py,sha256=i0VE07eU3ALIfi7wEnUfLMK6x85SjrMqIif-LyXQ-i8,13370
5
5
  crypticorn_utils/decorators.py,sha256=chsbF27_q3hC0egBaZLfv2vcqUcBSfQXRkLi3Ewb-20,1063
6
- crypticorn_utils/enums.py,sha256=KU1W98dkAPQ7-9YAsEhjUwX-Qd79Fze5dzPRP6MkBks,5025
6
+ crypticorn_utils/enums.py,sha256=gIiEhMJDTla4HnW1AcGYHG2IA_VAby-Nu8BFHyFlDxM,4955
7
7
  crypticorn_utils/errors.py,sha256=mzQZsD8mFPQY6RJLqN_yw9n4CNoo3pEsNh8-v8n_n_o,30440
8
8
  crypticorn_utils/exceptions.py,sha256=iHJOtEBrqgzZVa3ZC60TVYpEfUnhxzos_A9QefmVXT8,6633
9
9
  crypticorn_utils/logging.py,sha256=510DSQr4g5UbweSvZAxjdUKtmj-LU3Dw0clARaAvsZA,4439
@@ -14,17 +14,11 @@ crypticorn_utils/openapi.py,sha256=D8bCpCVVzYQptHrJ7SYOgCxI3R_d0cjW9KMOBq-x0xk,2
14
14
  crypticorn_utils/pagination.py,sha256=06shym28tfD-Cc9EPk2W7K2GHqUj2Kk4ShQ0BrKCDWY,11432
15
15
  crypticorn_utils/utils.py,sha256=DXIOjJCdrDqoNCLAtohWYrFnEd1nvmg9NXdL07yWAgk,3137
16
16
  crypticorn_utils/warnings.py,sha256=ErEB108UVZgV5ykSuFkjQgDTb9sb_T9yKq-h753Kw64,3085
17
- crypticorn_utils/cli/__init__.py,sha256=5qIQ_WXKSoJk3tGU6n0XwmSX8a2cDRSM6dncaU1qOZ4,123
18
- crypticorn_utils/cli/__main__.py,sha256=q_3MdBGUJPukr_xexup3TICf8kLL7Tp4JxLWB5XCESs,303
19
- crypticorn_utils/cli/init.py,sha256=Ubu0eOwUqVweb4EzqsceV4K5VMN2qz6_Riy9h2Pjb64,4099
20
- crypticorn_utils/cli/version.py,sha256=OVDxeL80eMgZsFgw2cDSzFfuaRToDfnYAVOQTpkoMWs,206
21
- crypticorn_utils/cli/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- crypticorn_utils/cli/templates/auth.py,sha256=i27-Ts-Eiyv6_WRshOp7NV5OYUNbw2-kiN5Ll0k2UOA,839
23
17
  crypticorn_utils/router/admin_router.py,sha256=B8s0hqOFe6CFTTsCxtwMUZSdb5EbRsFeKPixH9CXwOU,4090
24
18
  crypticorn_utils/router/status_router.py,sha256=tqrTXq4ZWCxiTXinQoK-2tadWu2jUFzzzkN_Dft0P8g,1084
25
- crypticorn_utils-0.1.1rc1.dist-info/licenses/LICENSE,sha256=HonAVvzFXkP2C1d7D3ByIKPwjGH8NcHTAQvKH7uvOHQ,1856
26
- crypticorn_utils-0.1.1rc1.dist-info/METADATA,sha256=Wf5cqB8qnEnu20zpOqwcOt1nY4mYkLckFCwY3pb011w,4377
27
- crypticorn_utils-0.1.1rc1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
28
- crypticorn_utils-0.1.1rc1.dist-info/entry_points.txt,sha256=G4yWDll7v_Kb4uemqI-_qTKXHvs10R4mD7hNuD-biv4,71
29
- crypticorn_utils-0.1.1rc1.dist-info/top_level.txt,sha256=jLPvdxnI36RPf8TX3aZhl35OSd721xEYyFSEeQKF6Ic,17
30
- crypticorn_utils-0.1.1rc1.dist-info/RECORD,,
19
+ crypticorn_utils-0.1.2.dist-info/licenses/LICENSE,sha256=HonAVvzFXkP2C1d7D3ByIKPwjGH8NcHTAQvKH7uvOHQ,1856
20
+ crypticorn_utils-0.1.2.dist-info/METADATA,sha256=A3c5MN2-lQh32s5Tsdv-oX3Xp5NxIQrXLndUExIr7dc,4933
21
+ crypticorn_utils-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
+ crypticorn_utils-0.1.2.dist-info/entry_points.txt,sha256=G4yWDll7v_Kb4uemqI-_qTKXHvs10R4mD7hNuD-biv4,71
23
+ crypticorn_utils-0.1.2.dist-info/top_level.txt,sha256=jLPvdxnI36RPf8TX3aZhl35OSd721xEYyFSEeQKF6Ic,17
24
+ crypticorn_utils-0.1.2.dist-info/RECORD,,
@@ -1,4 +0,0 @@
1
- from crypticorn.cli.init import init_group
2
- from crypticorn.cli.version import version
3
-
4
- __all__ = ["init_group", "version"]
@@ -1,17 +0,0 @@
1
- # crypticorn/cli.py
2
-
3
- import click
4
- from crypticorn.cli import init_group, version
5
-
6
-
7
- @click.group()
8
- def cli():
9
- """🧙 Crypticorn CLI — magic for our microservices."""
10
- pass
11
-
12
-
13
- cli.add_command(init_group, name="init")
14
- cli.add_command(version, name="version")
15
-
16
- if __name__ == "__main__":
17
- cli()
@@ -1,127 +0,0 @@
1
- import click
2
- from pathlib import Path
3
- import subprocess
4
- import importlib.resources
5
- import crypticorn.cli.templates as templates
6
-
7
-
8
- def get_git_root() -> Path:
9
- """Get the root directory of the git repository."""
10
- try:
11
- return Path(
12
- subprocess.check_output(
13
- ["git", "rev-parse", "--show-toplevel"], text=True
14
- ).strip()
15
- )
16
- except Exception:
17
- return Path.cwd()
18
-
19
-
20
- def copy_template(template_name: str, target_path: Path):
21
- """Copy a template file to the target path."""
22
- with importlib.resources.files(templates).joinpath(template_name).open(
23
- "r"
24
- ) as template_file:
25
- content = template_file.read()
26
-
27
- target_path.parent.mkdir(parents=True, exist_ok=True)
28
- with target_path.open("w") as f:
29
- f.write(content)
30
-
31
- click.secho(f"✅ Created: {target_path}", fg="green")
32
-
33
-
34
- def check_file_exists(path: Path, force: bool):
35
- if path.exists() and not force:
36
- click.secho("File already exists, use --force / -f to overwrite", fg="red")
37
- return False
38
- return True
39
-
40
-
41
- @click.group()
42
- def init_group():
43
- """Initialize files like CI configs, linters, etc."""
44
- pass
45
-
46
-
47
- @init_group.command("ruff")
48
- @click.option("-f", "--force", is_flag=True, help="Force overwrite the ruff.yml")
49
- def init_ruff(force):
50
- """Add .github/workflows/ruff.yml"""
51
- root = get_git_root()
52
- target = root / ".github/workflows/ruff.yml"
53
- if target.exists() and not force:
54
- click.secho("File already exists, use --force / -f to overwrite", fg="red")
55
- return
56
- copy_template("ruff.yml", target)
57
-
58
-
59
- @init_group.command("docker")
60
- @click.option(
61
- "-o", "--output", type=click.Path(), help="Custom output path for the Dockerfile"
62
- )
63
- @click.option("-f", "--force", is_flag=True, help="Force overwrite the Dockerfile")
64
- def init_docker(output, force):
65
- """Add Dockerfile"""
66
- root = get_git_root()
67
- if output and Path(output).is_file():
68
- click.secho("Output path is a file, please provide a directory path", fg="red")
69
- return
70
- target = (Path(output) if output else root) / "Dockerfile"
71
- if not check_file_exists(target, force):
72
- return
73
- copy_template("Dockerfile", target)
74
- click.secho("Make sure to update the Dockerfile", fg="yellow")
75
-
76
-
77
- @init_group.command("auth")
78
- @click.option(
79
- "-o", "--output", type=click.Path(), help="Custom output path for the auth handler"
80
- )
81
- @click.option("-f", "--force", is_flag=True, help="Force overwrite the auth handler")
82
- def init_auth(output, force):
83
- """Add auth.py with auth handler. Everything you need to start using the auth service."""
84
- root = get_git_root()
85
- if output and Path(output).is_file():
86
- click.secho("Output path is a file, please provide a directory path", fg="red")
87
- return
88
- target = (Path(output) if output else root) / "auth.py"
89
- if not check_file_exists(target, force):
90
- return
91
- copy_template("auth.py", target)
92
- click.secho(
93
- """
94
- Make sure to update the .env and .env.example files with:
95
- IS_DOCKER=0
96
- API_ENV=local
97
- and the docker-compose.yml file with:
98
- environment:
99
- - IS_DOCKER=1
100
- and the .github/workflows/main.yaml file with:
101
- env:
102
- API_ENV: ${{ github.ref == 'refs/heads/main' && 'prod' || 'dev' }}
103
- """,
104
- fg="yellow",
105
- )
106
-
107
-
108
- @init_group.command("dependabot")
109
- @click.option("-f", "--force", is_flag=True, help="Force overwrite the dependabot.yml")
110
- def init_dependabot(force):
111
- """Add dependabot.yml"""
112
- root = get_git_root()
113
- target = root / ".github/dependabot.yml"
114
- if not check_file_exists(target, force):
115
- return
116
- copy_template("dependabot.yml", target)
117
-
118
-
119
- @init_group.command("merge-env")
120
- @click.option("-f", "--force", is_flag=True, help="Force overwrite the .env file")
121
- def init_merge_env(force):
122
- """Add script to merge environment and file variables into .env"""
123
- root = get_git_root()
124
- target = root / "scripts/merge-env.sh"
125
- if not check_file_exists(target, force):
126
- return
127
- copy_template("merge-env.sh", target)
File without changes
@@ -1,33 +0,0 @@
1
- from crypticorn.common import (
2
- AuthHandler as AuthHandler,
3
- Scope as Scope,
4
- Verify200Response as Verify200Response,
5
- BaseUrl as BaseUrl,
6
- ApiEnv as ApiEnv,
7
- )
8
- from fastapi import Security as Security
9
- import os
10
- import dotenv
11
- import logging
12
-
13
- dotenv.load_dotenv()
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
- DOCKER_ENV = os.getenv("IS_DOCKER", "0")
18
- API_ENV = os.getenv("API_ENV")
19
-
20
- if not API_ENV:
21
- raise ValueError(
22
- "API_ENV is not set. Please set it to 'prod', 'dev' or 'local' in .env (of type ApiEnv)."
23
- )
24
-
25
- if DOCKER_ENV == "0":
26
- logger.info(f"Using {API_ENV} environment")
27
- base_url = BaseUrl.from_env(ApiEnv(API_ENV))
28
- else:
29
- base_url = BaseUrl.DOCKER
30
- logger.info("Using docker environment")
31
-
32
- auth_handler = AuthHandler(base_url=base_url)
33
- logger.info(f"Auth URL: {auth_handler.client.config.host}")
@@ -1,8 +0,0 @@
1
- import importlib.metadata
2
- import click
3
-
4
-
5
- @click.command("version")
6
- def version():
7
- """Print the version of the crypticorn package"""
8
- click.echo(importlib.metadata.distribution("crypticorn").version)