laniakea-api-server 0.2.6__tar.gz → 0.2.8__tar.gz

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.
Files changed (24) hide show
  1. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/PKG-INFO +1 -1
  2. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/auth.py +21 -32
  3. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/config.py +2 -1
  4. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/queue.py +4 -4
  5. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/pyproject.toml +1 -1
  6. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/.github/workflows/publish.yml +0 -0
  7. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/.gitignore +0 -0
  8. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/LICENSE +0 -0
  9. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/README.md +0 -0
  10. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/__init__.py +0 -0
  11. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/cli.py +0 -0
  12. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/credential_parser.py +0 -0
  13. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/database.py +0 -0
  14. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/installer.py +0 -0
  15. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/main.py +0 -0
  16. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/models.py +0 -0
  17. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/routers/.health.py.swp +0 -0
  18. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/routers/__init__.py +0 -0
  19. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/routers/agent.py +0 -0
  20. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/routers/agents.py +0 -0
  21. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/routers/credentials.py +0 -0
  22. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/routers/deployments.py +0 -0
  23. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/routers/health.py +0 -0
  24. {laniakea_api_server-0.2.6 → laniakea_api_server-0.2.8}/laniakea_api/routers/tfstate.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: laniakea-api-server
3
- Version: 0.2.6
3
+ Version: 0.2.8
4
4
  Summary: Laniakea Queue API — OIDC-authenticated gateway for cloud deployment orchestration
5
5
  Project-URL: Homepage, https://github.com/laniakea/laniakea-api
6
6
  Project-URL: Issues, https://github.com/laniakea/laniakea-api/issues
@@ -10,45 +10,34 @@ import httpx
10
10
  import jwt
11
11
  from fastapi import Depends, HTTPException, status
12
12
  from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
13
- from laniakea_api.config import (
14
- SECRET_KEY, ALGORITHM, SESSION_TTL_MINUTES,
15
- OIDC_DISCOVERY_URL, AGENT_MASTER_PASSWORD,
16
- )
13
+ from laniakea_api.config import (SECRET_KEY, ALGORITHM, SESSION_TTL_MINUTES,
14
+ OIDC_DISCOVERY_URLS, AGENT_MASTER_PASSWORD)
15
+
17
16
 
18
17
  # Initialize security OAuth2 Bearer Token
19
18
  _bearer = HTTPBearer()
20
19
 
21
20
  async def fetch_userinfo(oidc_token: str) -> dict:
22
21
  """
23
- Takes an authentication token provided by a user and asks an external identity server
24
- (the OIDC Provider) who that user actually is, retriving user info
22
+ Validate the token against every trusted issuer, first match wins.
23
+ Supports federated logins (IAM ReCaS + Keycloak).
25
24
  """
26
- try:
27
- # asynchronous HTTP to avoid waste of server resources
28
- async with httpx.AsyncClient(timeout=10) as client:
29
- # OIDC discovery
30
- discovery = await client.get(OIDC_DISCOVERY_URL)
31
- discovery.raise_for_status() # status 200: OK
32
- userinfo_url = discovery.json()["userinfo_endpoint"] # now final url is known
33
- # USER info request
34
- resp = await client.get(
35
- userinfo_url,
36
- headers={"Authorization": f"Bearer {oidc_token}"},
37
- )
38
- if resp.status_code != 200:
39
- raise HTTPException(
40
- status_code=status.HTTP_401_UNAUTHORIZED,
41
- detail=f"OIDC userinfo returned {resp.status_code}",
42
- )
43
- return resp.json()
44
- except HTTPException:
45
- raise
46
- except Exception as exc:
47
- raise HTTPException(
48
- status_code=status.HTTP_401_UNAUTHORIZED,
49
- detail=f"OIDC validation failed: {exc}",
50
- )
51
-
25
+ last_status = None
26
+ async with httpx.AsyncClient(timeout=10) as client:
27
+ for discovery_url in OIDC_DISCOVERY_URLS:
28
+ try:
29
+ discovery = await client.get(discovery_url)
30
+ discovery.raise_for_status()
31
+ userinfo_url = discovery.json()["userinfo_endpoint"]
32
+ resp = await client.get(userinfo_url,
33
+ headers={"Authorization": f"Bearer {oidc_token}"})
34
+ if resp.status_code == 200:
35
+ return resp.json()
36
+ last_status = resp.status_code
37
+ except Exception:
38
+ continue
39
+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
40
+ detail=f"Token rejected by all trusted issuers (last: {last_status})")
52
41
 
53
42
  def create_session_token(user_info: dict) -> tuple:
54
43
  """
@@ -9,7 +9,8 @@ import os
9
9
  SECRET_KEY = os.getenv("SECRET_KEY", "")
10
10
  ALGORITHM = "HS256"
11
11
  SESSION_TTL_MINUTES = int(os.getenv("SESSION_TTL_MINUTES", "60")) # NOTE: maybe longer
12
- OIDC_DISCOVERY_URL = os.getenv("OIDC_DISCOVERY_URL", "")
12
+ #OIDC_DISCOVERY_URL = os.getenv("OIDC_DISCOVERY_URL", "")
13
+ OIDC_DISCOVERY_URLS = [u.strip() for u in os.getenv("OIDC_DISCOVERY_URL", "").split(",") if u.strip()]
13
14
 
14
15
  # Agent pool password
15
16
  # to revoke ALL agents: change this value and restart API + all agents.
@@ -120,7 +120,7 @@ def _sc_path(user_sub: str, name: str = "") -> str:
120
120
  return f"{base}/{name}" if name else base
121
121
 
122
122
  def vault_list_service_creds(user_sub: str) -> list:
123
- client = get_vault_client()
123
+ client = vault_client()
124
124
  try:
125
125
  resp = client.secrets.kv.v2.list_secrets(path=_sc_path(user_sub), mount_point=VAULT_MOUNT)
126
126
  names = [k.rstrip("/") for k in resp["data"]["keys"]]
@@ -133,7 +133,7 @@ def vault_list_service_creds(user_sub: str) -> list:
133
133
  return out
134
134
 
135
135
  def vault_read_service_creds(user_sub: str, name: str) -> dict:
136
- client = get_vault_client()
136
+ client = vault_client()
137
137
  try:
138
138
  resp = client.secrets.kv.v2.read_secret_version(path=_sc_path(user_sub, name), mount_point=VAULT_MOUNT)
139
139
  return resp["data"]["data"] or {}
@@ -141,11 +141,11 @@ def vault_read_service_creds(user_sub: str, name: str) -> dict:
141
141
  return {}
142
142
 
143
143
  def vault_write_service_creds(user_sub: str, name: str, data: dict) -> None:
144
- client = get_vault_client()
144
+ client = vault_client()
145
145
  client.secrets.kv.v2.create_or_update_secret(
146
146
  path=_sc_path(user_sub, name), secret=data, mount_point=VAULT_MOUNT)
147
147
 
148
148
  def vault_delete_service_creds(user_sub: str, name: str) -> None:
149
- client = get_vault_client()
149
+ client = vault_client()
150
150
  client.secrets.kv.v2.delete_metadata_and_all_versions(
151
151
  path=_sc_path(user_sub, name), mount_point=VAULT_MOUNT)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "laniakea-api-server"
7
- version = "0.2.6"
7
+ version = "0.2.8"
8
8
  description = "Laniakea Queue API — OIDC-authenticated gateway for cloud deployment orchestration"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }