arkitekt-next 0.15.0__py3-none-any.whl → 0.17.0__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.
Potentially problematic release.
This version of arkitekt-next might be problematic. Click here for more details.
- arkitekt_next/app/fakts.py +27 -1
- arkitekt_next/cli/commands/call/local.py +1 -1
- arkitekt_next/cli/commands/call/main.py +14 -7
- arkitekt_next/cli/templates/filter.py +7 -7
- arkitekt_next/cli/templates/simple.py +4 -1
- arkitekt_next/py.typed +0 -0
- {arkitekt_next-0.15.0.dist-info → arkitekt_next-0.17.0.dist-info}/METADATA +3 -3
- {arkitekt_next-0.15.0.dist-info → arkitekt_next-0.17.0.dist-info}/RECORD +11 -57
- arkitekt_next/__blok__.py +0 -58
- arkitekt_next/bloks/__init__.py +0 -1
- arkitekt_next/bloks/admin.py +0 -43
- arkitekt_next/bloks/alpaka.py +0 -136
- arkitekt_next/bloks/arkitekt.py +0 -55
- arkitekt_next/bloks/base.py +0 -215
- arkitekt_next/bloks/channel.py +0 -27
- arkitekt_next/bloks/config.py +0 -43
- arkitekt_next/bloks/elektro.py +0 -42
- arkitekt_next/bloks/fluss.py +0 -33
- arkitekt_next/bloks/gateway.py +0 -230
- arkitekt_next/bloks/internal_docker.py +0 -95
- arkitekt_next/bloks/kabinet.py +0 -46
- arkitekt_next/bloks/kraph.py +0 -45
- arkitekt_next/bloks/l.py +0 -136
- arkitekt_next/bloks/livekit.py +0 -89
- arkitekt_next/bloks/lok.py +0 -354
- arkitekt_next/bloks/lovekit.py +0 -199
- arkitekt_next/bloks/mikro.py +0 -42
- arkitekt_next/bloks/minio.py +0 -176
- arkitekt_next/bloks/mount.py +0 -34
- arkitekt_next/bloks/namegen.py +0 -34
- arkitekt_next/bloks/ollama.py +0 -94
- arkitekt_next/bloks/orkestrator.py +0 -122
- arkitekt_next/bloks/postgres.py +0 -136
- arkitekt_next/bloks/redis.py +0 -79
- arkitekt_next/bloks/rekuest.py +0 -30
- arkitekt_next/bloks/secret.py +0 -32
- arkitekt_next/bloks/self_signed.py +0 -91
- arkitekt_next/bloks/services/__init__.py +0 -27
- arkitekt_next/bloks/services/admin.py +0 -21
- arkitekt_next/bloks/services/certer.py +0 -14
- arkitekt_next/bloks/services/channel.py +0 -12
- arkitekt_next/bloks/services/config.py +0 -13
- arkitekt_next/bloks/services/db.py +0 -23
- arkitekt_next/bloks/services/gateway.py +0 -29
- arkitekt_next/bloks/services/livekit.py +0 -20
- arkitekt_next/bloks/services/lok.py +0 -20
- arkitekt_next/bloks/services/mount.py +0 -9
- arkitekt_next/bloks/services/name.py +0 -11
- arkitekt_next/bloks/services/ollama.py +0 -21
- arkitekt_next/bloks/services/redis.py +0 -19
- arkitekt_next/bloks/services/s3.py +0 -21
- arkitekt_next/bloks/services/secret.py +0 -16
- arkitekt_next/bloks/services/socket.py +0 -13
- arkitekt_next/bloks/socket.py +0 -40
- arkitekt_next/bloks/tailscale.py +0 -90
- {arkitekt_next-0.15.0.dist-info → arkitekt_next-0.17.0.dist-info}/WHEEL +0 -0
- {arkitekt_next-0.15.0.dist-info → arkitekt_next-0.17.0.dist-info}/entry_points.txt +0 -0
- {arkitekt_next-0.15.0.dist-info → arkitekt_next-0.17.0.dist-info}/licenses/LICENSE +0 -0
arkitekt_next/bloks/kabinet.py
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
from typing import Dict, Any
|
|
2
|
-
import secrets
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
from blok import blok, InitContext, ExecutionContext, Option
|
|
6
|
-
from blok.tree import Repo, YamlFile
|
|
7
|
-
from arkitekt_next.bloks.base import BaseArkitektService
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@blok("live.arkitekt.kabinet", description="a container and app management service")
|
|
11
|
-
class KabinetBlok(BaseArkitektService):
|
|
12
|
-
def __init__(self) -> None:
|
|
13
|
-
self.dev = False
|
|
14
|
-
self.host = "kabinet"
|
|
15
|
-
self.command = "bash run-debug.sh"
|
|
16
|
-
self.repo = "https://github.com/arkitektio/kabinet-server"
|
|
17
|
-
self.scopes = {
|
|
18
|
-
"kabinet_deploy": "Deploy containers",
|
|
19
|
-
"kabinet_add_repo": "Add repositories to the database",
|
|
20
|
-
}
|
|
21
|
-
self.mount_repo = False
|
|
22
|
-
self.build_repo = False
|
|
23
|
-
self.buckets = ["media"]
|
|
24
|
-
self.secret_key = secrets.token_hex(16)
|
|
25
|
-
self.ensured_repos = ["jhnnsrs/ome:main", "jhnnsrs/renderer:main"]
|
|
26
|
-
self.image = "jhnnsrs/kabinet:nightly"
|
|
27
|
-
|
|
28
|
-
def get_additional_config(self):
|
|
29
|
-
return {"ensured_repos": self.ensured_repos}
|
|
30
|
-
|
|
31
|
-
def get_builder(self):
|
|
32
|
-
return "arkitekt.generic"
|
|
33
|
-
|
|
34
|
-
def build(self, context: ExecutionContext):
|
|
35
|
-
context.docker_compose.set_nested("services", self.host, self.service)
|
|
36
|
-
|
|
37
|
-
def get_additional_options(self):
|
|
38
|
-
with_repos = Option(
|
|
39
|
-
subcommand="repos",
|
|
40
|
-
help="The default repos to enable for the service",
|
|
41
|
-
default=self.secret_key,
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
return [
|
|
45
|
-
with_repos,
|
|
46
|
-
]
|
arkitekt_next/bloks/kraph.py
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import click
|
|
2
|
-
from pydantic import BaseModel
|
|
3
|
-
from typing import Dict, Any
|
|
4
|
-
import yaml
|
|
5
|
-
import secrets
|
|
6
|
-
from blok import blok, InitContext
|
|
7
|
-
|
|
8
|
-
from blok import blok, InitContext, ExecutionContext, Option
|
|
9
|
-
from blok.tree import YamlFile, Repo
|
|
10
|
-
from arkitekt_next.bloks.base import BaseArkitektService
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class AccessCredentials(BaseModel):
|
|
14
|
-
password: str
|
|
15
|
-
username: str
|
|
16
|
-
host: str
|
|
17
|
-
port: str
|
|
18
|
-
db_name: str
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@blok(
|
|
22
|
-
"live.arkitekt.kraph",
|
|
23
|
-
description="Kraph allows you to interconnect structures in a graph database",
|
|
24
|
-
)
|
|
25
|
-
class KraphBlok(BaseArkitektService):
|
|
26
|
-
def __init__(self) -> None:
|
|
27
|
-
self.dev = False
|
|
28
|
-
self.host = "kraph"
|
|
29
|
-
self.command = "bash run-debug.sh"
|
|
30
|
-
self.repo = "https://github.com/arkitektio/kraph-server"
|
|
31
|
-
self.scopes = {
|
|
32
|
-
"kraph_read": "Read from the graph database",
|
|
33
|
-
"mikro_write": "Write image to the database",
|
|
34
|
-
}
|
|
35
|
-
self.image = "jhnnsrs/kraph:nightly"
|
|
36
|
-
self.mount_repo = False
|
|
37
|
-
self.build_repo = False
|
|
38
|
-
self.buckets = ["media"]
|
|
39
|
-
self.secret_key = secrets.token_hex(16)
|
|
40
|
-
|
|
41
|
-
def get_builder(self):
|
|
42
|
-
return "arkitekt.generic"
|
|
43
|
-
|
|
44
|
-
def build(self, context: ExecutionContext):
|
|
45
|
-
context.docker_compose.set_nested("services", self.host, self.service)
|
arkitekt_next/bloks/l.py
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
from dataclasses import asdict
|
|
2
|
-
from typing import Dict, Any
|
|
3
|
-
import secrets
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
from arkitekt_next.bloks.services.admin import AdminService
|
|
7
|
-
from arkitekt_next.bloks.services.channel import ChannelService
|
|
8
|
-
from arkitekt_next.bloks.services.config import ConfigService
|
|
9
|
-
from arkitekt_next.bloks.services.db import DBService
|
|
10
|
-
from arkitekt_next.bloks.services.gateway import GatewayService
|
|
11
|
-
from arkitekt_next.bloks.services.lok import LokService
|
|
12
|
-
from arkitekt_next.bloks.services.mount import MountService
|
|
13
|
-
from arkitekt_next.bloks.services.ollama import OllamaService
|
|
14
|
-
from arkitekt_next.bloks.services.redis import RedisService
|
|
15
|
-
from arkitekt_next.bloks.services.s3 import S3Service
|
|
16
|
-
from arkitekt_next.bloks.services.secret import SecretService
|
|
17
|
-
from blok import blok, InitContext, ExecutionContext, Option
|
|
18
|
-
from blok.bloks.services.dns import DnsService
|
|
19
|
-
from blok.tree import Repo, YamlFile
|
|
20
|
-
from arkitekt_next.bloks.base import BaseArkitektService
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@blok("live.arkitekt.alpaka", description="a container and app management service")
|
|
24
|
-
class AlpakaBlok(BaseArkitektService):
|
|
25
|
-
def get_builder(self):
|
|
26
|
-
return "arkitekt.generic"
|
|
27
|
-
|
|
28
|
-
def __init__(self) -> None:
|
|
29
|
-
self.dev = False
|
|
30
|
-
self.host = "alpaka"
|
|
31
|
-
self.command = "bash run-debug.sh"
|
|
32
|
-
self.repo = "https://github.com/arkitektio/alpaka-server"
|
|
33
|
-
self.scopes = {
|
|
34
|
-
"alpaka_pull": "Pull new Models",
|
|
35
|
-
"alpaka_chat": "Add repositories to the database",
|
|
36
|
-
}
|
|
37
|
-
self.mount_repo = False
|
|
38
|
-
self.build_repo = False
|
|
39
|
-
self.buckets = ["media"]
|
|
40
|
-
self.secret_key = secrets.token_hex(16)
|
|
41
|
-
self.image = "jhnnsrs/alpaka:nightly"
|
|
42
|
-
|
|
43
|
-
def preflight(
|
|
44
|
-
self,
|
|
45
|
-
lok: LokService,
|
|
46
|
-
db: DBService,
|
|
47
|
-
redis: RedisService,
|
|
48
|
-
s3: S3Service,
|
|
49
|
-
config: ConfigService,
|
|
50
|
-
ollama: OllamaService,
|
|
51
|
-
mount: MountService,
|
|
52
|
-
admin: AdminService,
|
|
53
|
-
secret: SecretService,
|
|
54
|
-
gateway: GatewayService,
|
|
55
|
-
mount_repo: bool = False,
|
|
56
|
-
host: str = "",
|
|
57
|
-
image: str = "",
|
|
58
|
-
secret_key: str = "",
|
|
59
|
-
build_repo: bool = False,
|
|
60
|
-
command: str = "",
|
|
61
|
-
repo: str = "",
|
|
62
|
-
disable: bool = False,
|
|
63
|
-
dev: bool = False,
|
|
64
|
-
):
|
|
65
|
-
lok.register_scopes(self.scopes)
|
|
66
|
-
|
|
67
|
-
path_name = self.host
|
|
68
|
-
|
|
69
|
-
gateway.expose(path_name, 80, self.host)
|
|
70
|
-
|
|
71
|
-
postgress_access = db.register_db(self.host)
|
|
72
|
-
redis_access = redis.register()
|
|
73
|
-
lok_access = lok.retrieve_credentials()
|
|
74
|
-
admin_access = admin.retrieve()
|
|
75
|
-
minio_access = s3.create_buckets(self.buckets)
|
|
76
|
-
lok_labels = lok.retrieve_labels("live.arkitekt.alpaka", self.get_builder())
|
|
77
|
-
ollama_access = ollama.get_access()
|
|
78
|
-
|
|
79
|
-
django_secret = secret.retrieve_secret()
|
|
80
|
-
|
|
81
|
-
csrf_trusted_origins = []
|
|
82
|
-
|
|
83
|
-
configuration = YamlFile(
|
|
84
|
-
**{
|
|
85
|
-
"db": asdict(postgress_access),
|
|
86
|
-
"django": {
|
|
87
|
-
"admin": asdict(admin_access),
|
|
88
|
-
"debug": True,
|
|
89
|
-
"hosts": ["*"],
|
|
90
|
-
"secret_key": django_secret,
|
|
91
|
-
},
|
|
92
|
-
"redis": asdict(redis_access),
|
|
93
|
-
"lok": asdict(lok_access),
|
|
94
|
-
"s3": asdict(minio_access),
|
|
95
|
-
"scopes": self.scopes,
|
|
96
|
-
"force_script_name": path_name,
|
|
97
|
-
"csrf_trusted_origins": csrf_trusted_origins,
|
|
98
|
-
**self.get_additional_config(),
|
|
99
|
-
}
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
config_mount = config.register_config(f"{self.host}.yaml", configuration)
|
|
103
|
-
|
|
104
|
-
depends_on = []
|
|
105
|
-
|
|
106
|
-
if redis_access.dependency:
|
|
107
|
-
depends_on.append(redis_access.dependency)
|
|
108
|
-
|
|
109
|
-
if postgress_access.dependency:
|
|
110
|
-
depends_on.append(postgress_access.dependency)
|
|
111
|
-
|
|
112
|
-
if minio_access.dependency:
|
|
113
|
-
depends_on.append(minio_access.dependency)
|
|
114
|
-
|
|
115
|
-
if ollama_access.dependency:
|
|
116
|
-
depends_on.append(ollama_access.dependency)
|
|
117
|
-
|
|
118
|
-
service = {
|
|
119
|
-
"labels": lok_labels,
|
|
120
|
-
"volumes": [f"{config_mount}:/workspace/config.yaml"],
|
|
121
|
-
"depends_on": depends_on,
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if mount_repo or dev:
|
|
125
|
-
mount = mount.register_mount(self.host, Repo(repo))
|
|
126
|
-
service["volumes"].extend([f"{mount}:/workspace"])
|
|
127
|
-
|
|
128
|
-
if build_repo or dev:
|
|
129
|
-
mount = mount.register_mount(self.host, Repo(repo))
|
|
130
|
-
service["build"] = mount
|
|
131
|
-
else:
|
|
132
|
-
service["image"] = image
|
|
133
|
-
|
|
134
|
-
service["command"] = command
|
|
135
|
-
|
|
136
|
-
self.service = service
|
arkitekt_next/bloks/livekit.py
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
from typing import Dict, Any
|
|
2
|
-
import secrets
|
|
3
|
-
|
|
4
|
-
from arkitekt_next.bloks.services.gateway import GatewayService
|
|
5
|
-
from arkitekt_next.bloks.services.livekit import LivekitService, LivekitCredentials
|
|
6
|
-
from blok import blok, InitContext, ExecutionContext, Option
|
|
7
|
-
from blok.tree import YamlFile, Repo
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@blok(LivekitService, description="a self-hosted livekit server")
|
|
11
|
-
class LocalLiveKitBlok:
|
|
12
|
-
def __init__(self) -> None:
|
|
13
|
-
self.host = "livekit"
|
|
14
|
-
self.command = ["--dev", "--bind", "0.0.0.0"]
|
|
15
|
-
self.image = "livekit/livekit-server:latest"
|
|
16
|
-
self.mount_repo = True
|
|
17
|
-
self.build_repo = True
|
|
18
|
-
self.secret_key = secrets.token_hex(16)
|
|
19
|
-
self.ensured_repos = []
|
|
20
|
-
self.port_range = [50000, 50030]
|
|
21
|
-
self.api_key = "devkey"
|
|
22
|
-
self.api_secret = "secret"
|
|
23
|
-
self.skip = False
|
|
24
|
-
self.api_port = 7880
|
|
25
|
-
|
|
26
|
-
def preflight(self, init: InitContext, gateway: GatewayService):
|
|
27
|
-
for key, value in init.kwargs.items():
|
|
28
|
-
setattr(self, key, value)
|
|
29
|
-
|
|
30
|
-
deps = init.dependencies
|
|
31
|
-
|
|
32
|
-
if self.skip:
|
|
33
|
-
return
|
|
34
|
-
|
|
35
|
-
gateway.expose_port(7880, self.host, True)
|
|
36
|
-
gateway.expose_port_to(7882, self.host, 7880, False)
|
|
37
|
-
|
|
38
|
-
self.initialized = True
|
|
39
|
-
|
|
40
|
-
def get_access(self):
|
|
41
|
-
return LivekitCredentials(
|
|
42
|
-
**{
|
|
43
|
-
"api_key": self.api_key,
|
|
44
|
-
"api_secret": self.api_secret,
|
|
45
|
-
"host": self.host,
|
|
46
|
-
"port": self.api_port,
|
|
47
|
-
}
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
def build(self, context: ExecutionContext):
|
|
51
|
-
if self.skip:
|
|
52
|
-
return
|
|
53
|
-
db_service = {
|
|
54
|
-
"labels": [
|
|
55
|
-
"fakts.service=io.livekit.livekit",
|
|
56
|
-
"fakts.builder=livekitio.livekit",
|
|
57
|
-
],
|
|
58
|
-
"image": self.image,
|
|
59
|
-
"command": self.command,
|
|
60
|
-
"ports": [
|
|
61
|
-
f"{self.port_range[0]}-{self.port_range[1]}:{self.port_range[0]}-{self.port_range[1]}",
|
|
62
|
-
"7881:7881",
|
|
63
|
-
],
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
context.docker_compose.set_nested("services", self.host, db_service)
|
|
67
|
-
|
|
68
|
-
def get_options(self):
|
|
69
|
-
with_host = Option(
|
|
70
|
-
subcommand="host",
|
|
71
|
-
help="The fakts url for connection",
|
|
72
|
-
default=self.host,
|
|
73
|
-
)
|
|
74
|
-
with_skip = Option(
|
|
75
|
-
subcommand="skip",
|
|
76
|
-
help="The fakts_next url for connection",
|
|
77
|
-
default=False,
|
|
78
|
-
type=bool,
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
return [
|
|
82
|
-
with_host,
|
|
83
|
-
with_skip,
|
|
84
|
-
]
|
|
85
|
-
|
|
86
|
-
def __str__(self) -> str:
|
|
87
|
-
return (
|
|
88
|
-
f"LiveKitBlok(host={self.host}, command={self.command}, image={self.image})"
|
|
89
|
-
)
|
arkitekt_next/bloks/lok.py
DELETED
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
import click
|
|
2
|
-
|
|
3
|
-
from pydantic import BaseModel
|
|
4
|
-
from cryptography.hazmat.primitives import serialization as crypto_serialization
|
|
5
|
-
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
6
|
-
from cryptography.hazmat.backends import default_backend as crypto_default_backend
|
|
7
|
-
from typing import Dict, Optional
|
|
8
|
-
from arkitekt_next.bloks.secret import SecretBlok
|
|
9
|
-
from arkitekt_next.bloks.services.admin import AdminService
|
|
10
|
-
from arkitekt_next.bloks.services.db import DBService
|
|
11
|
-
from arkitekt_next.bloks.services.s3 import S3Service
|
|
12
|
-
from arkitekt_next.bloks.services.gateway import GatewayService
|
|
13
|
-
from arkitekt_next.bloks.services.livekit import LivekitService
|
|
14
|
-
from arkitekt_next.bloks.services.lok import LokCredentials, LokService
|
|
15
|
-
import yaml
|
|
16
|
-
import secrets
|
|
17
|
-
from dataclasses import asdict
|
|
18
|
-
|
|
19
|
-
from arkitekt_next.bloks.services.redis import RedisService
|
|
20
|
-
from blok import blok, InitContext, ExecutionContext, Option
|
|
21
|
-
from blok.bloks.services.dns import DnsService
|
|
22
|
-
from blok.tree import YamlFile, Repo
|
|
23
|
-
from blok import blok, InitContext
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
DEFAULT_ARKITEKT_URL = "http://localhost:8000"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
# Define a custom user type that will parse and validate the user input
|
|
30
|
-
class UserParamType(click.ParamType):
|
|
31
|
-
name = "user"
|
|
32
|
-
|
|
33
|
-
def convert(self, value, param, ctx):
|
|
34
|
-
if isinstance(value, dict):
|
|
35
|
-
return value
|
|
36
|
-
try:
|
|
37
|
-
name, password = value.split(":")
|
|
38
|
-
return {"username": name, "password": password}
|
|
39
|
-
except ValueError:
|
|
40
|
-
self.fail(
|
|
41
|
-
f"User '{value}' is not in the correct format. It should be 'name:password'.",
|
|
42
|
-
param,
|
|
43
|
-
ctx,
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
USER = UserParamType()
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# Define a custom user type that will parse and validate the user input
|
|
51
|
-
class GroupParamType(click.ParamType):
|
|
52
|
-
name = "group"
|
|
53
|
-
|
|
54
|
-
def convert(self, value, param, ctx):
|
|
55
|
-
if isinstance(value, dict):
|
|
56
|
-
return value
|
|
57
|
-
try:
|
|
58
|
-
name, description = value.split(":")
|
|
59
|
-
return {"name": name, "description": description}
|
|
60
|
-
except ValueError:
|
|
61
|
-
self.fail(
|
|
62
|
-
f"User '{value}' is not in the correct format. It should be 'name:password'.",
|
|
63
|
-
param,
|
|
64
|
-
ctx,
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
GROUP = GroupParamType()
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class RedeemTokenParamType(click.ParamType):
|
|
72
|
-
name = "redeem_token"
|
|
73
|
-
|
|
74
|
-
def convert(self, value, param, ctx):
|
|
75
|
-
if isinstance(value, dict):
|
|
76
|
-
assert "user" in value, f"scope is required {value}"
|
|
77
|
-
assert "token" in value, f"description is required {value}"
|
|
78
|
-
return value
|
|
79
|
-
|
|
80
|
-
try:
|
|
81
|
-
user, token = value.split(":")
|
|
82
|
-
return {"user": user, "token": token}
|
|
83
|
-
except ValueError:
|
|
84
|
-
self.fail(
|
|
85
|
-
f"RedeemToken '{value}' is not in the correct format. It should be 'username:token'.",
|
|
86
|
-
param,
|
|
87
|
-
ctx,
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
TOKEN = RedeemTokenParamType()
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
class ScopeParamType(click.ParamType):
|
|
95
|
-
name = "scope"
|
|
96
|
-
|
|
97
|
-
def convert(self, value, param, ctx):
|
|
98
|
-
if isinstance(value, dict):
|
|
99
|
-
assert "scope" in value, f"scope is required {value}"
|
|
100
|
-
assert "description" in value, f"description is required {value}"
|
|
101
|
-
return value
|
|
102
|
-
|
|
103
|
-
try:
|
|
104
|
-
name, description = value.split(":")
|
|
105
|
-
return {"scope": name, "description": description}
|
|
106
|
-
except ValueError:
|
|
107
|
-
self.fail(
|
|
108
|
-
f"Scopes '{value}' is not in the correct format. It should be 'scope:description'.",
|
|
109
|
-
param,
|
|
110
|
-
ctx,
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
SCOPE = ScopeParamType()
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
@blok(LokService, description="Lok is an authentication and authorization service")
|
|
118
|
-
class LokBlok:
|
|
119
|
-
db_name: str
|
|
120
|
-
|
|
121
|
-
def __init__(self) -> None:
|
|
122
|
-
self.db_name = "lok_db"
|
|
123
|
-
self.mount_repo = False
|
|
124
|
-
self.build_repo = False
|
|
125
|
-
self.private_key = None
|
|
126
|
-
self.public_key = None
|
|
127
|
-
self.host = "lok"
|
|
128
|
-
self.dev = False
|
|
129
|
-
self.with_repo = False
|
|
130
|
-
self.command = "bash run-debug.sh"
|
|
131
|
-
self.repo = "https://github.com/jhnnsrs/lok-server-next"
|
|
132
|
-
self.image = "jhnnsrs/lok_next:nightly"
|
|
133
|
-
self.users = []
|
|
134
|
-
self.tokens = []
|
|
135
|
-
self.groups = []
|
|
136
|
-
self.secret_key = secrets.token_hex(16)
|
|
137
|
-
self.scopes = {
|
|
138
|
-
"openid": "The open id connect scope",
|
|
139
|
-
"read": "A generic read access",
|
|
140
|
-
"write": "A generic write access",
|
|
141
|
-
}
|
|
142
|
-
self.buckets = ["media"]
|
|
143
|
-
self.key = None
|
|
144
|
-
self.deployment_name = "default"
|
|
145
|
-
self.token_expiry_seconds = 700000
|
|
146
|
-
self.preformed_redeem_tokens = [secrets.token_hex(16) for i in range(80)]
|
|
147
|
-
self.registered_tokens = {}
|
|
148
|
-
self.local_access = None
|
|
149
|
-
|
|
150
|
-
def retrieve_credentials(self) -> LokCredentials:
|
|
151
|
-
return LokCredentials(
|
|
152
|
-
public_key=self.public_key, key_type="RS256", issuer="lok"
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
def retrieve_labels(self, service_name: str, builder_name: str) -> list[str]:
|
|
156
|
-
return [
|
|
157
|
-
f"fakts.service={service_name}",
|
|
158
|
-
f"fakts.builder={builder_name}",
|
|
159
|
-
]
|
|
160
|
-
|
|
161
|
-
def retrieve_token(self, user: str = "admin") -> str:
|
|
162
|
-
new_token = self.secret_blok.retrieve_secret()
|
|
163
|
-
self.registered_tokens[user] = new_token
|
|
164
|
-
|
|
165
|
-
return new_token
|
|
166
|
-
|
|
167
|
-
def register_scopes(self, scopes_dict: Dict[str, str]) -> LokCredentials:
|
|
168
|
-
self.scopes = self.scopes | scopes_dict
|
|
169
|
-
|
|
170
|
-
def preflight(
|
|
171
|
-
self,
|
|
172
|
-
init: InitContext,
|
|
173
|
-
gateway: GatewayService,
|
|
174
|
-
db: DBService,
|
|
175
|
-
redis: RedisService,
|
|
176
|
-
admin: AdminService,
|
|
177
|
-
secrets: SecretBlok,
|
|
178
|
-
s3: S3Service,
|
|
179
|
-
):
|
|
180
|
-
for key, value in init.kwargs.items():
|
|
181
|
-
setattr(self, key, value)
|
|
182
|
-
|
|
183
|
-
assert self.public_key, "Public key is required"
|
|
184
|
-
assert self.private_key, "Private key is required"
|
|
185
|
-
|
|
186
|
-
gateway.expose("lok", 80, self.host, strip_prefix=False)
|
|
187
|
-
gateway.expose_mapped(".well-known", 80, self.host, "lok")
|
|
188
|
-
|
|
189
|
-
self.secret_blok = secrets
|
|
190
|
-
self.postgress_access = db.register_db(self.host)
|
|
191
|
-
self.redis_access = redis.register()
|
|
192
|
-
self.admin_access = admin.retrieve()
|
|
193
|
-
self.s3_access = s3.create_buckets(self.buckets)
|
|
194
|
-
self.initialized = True
|
|
195
|
-
|
|
196
|
-
def build(self, context: ExecutionContext):
|
|
197
|
-
depends_on = []
|
|
198
|
-
|
|
199
|
-
if self.redis_access.dependency:
|
|
200
|
-
depends_on.append(self.redis_access.dependency)
|
|
201
|
-
|
|
202
|
-
if self.postgress_access.dependency:
|
|
203
|
-
depends_on.append(self.postgress_access.dependency)
|
|
204
|
-
|
|
205
|
-
if self.s3_access.dependency:
|
|
206
|
-
depends_on.append(self.s3_access.dependency)
|
|
207
|
-
|
|
208
|
-
db_service = {
|
|
209
|
-
"labels": [
|
|
210
|
-
"fakts.service=live.arkitekt.lok",
|
|
211
|
-
"fakts.builder=arkitekt.lok",
|
|
212
|
-
],
|
|
213
|
-
"depends_on": depends_on,
|
|
214
|
-
"volumes": [
|
|
215
|
-
"/var/run/docker.sock:/var/run/docker.sock",
|
|
216
|
-
"./configs/lok.yaml:/workspace/config.yaml",
|
|
217
|
-
],
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if self.mount_repo or self.dev:
|
|
221
|
-
context.file_tree.set_nested("mounts", "lok", Repo(self.repo))
|
|
222
|
-
db_service["volumes"].append("./mounts/lok:/workspace")
|
|
223
|
-
|
|
224
|
-
if self.build_repo or self.dev:
|
|
225
|
-
context.file_tree.set_nested("mounts", "lok", Repo(self.repo))
|
|
226
|
-
db_service["build"] = "./mounts/lok"
|
|
227
|
-
else:
|
|
228
|
-
db_service["image"] = self.image
|
|
229
|
-
|
|
230
|
-
db_service["command"] = self.command
|
|
231
|
-
|
|
232
|
-
trusted_origins = []
|
|
233
|
-
|
|
234
|
-
configuration = YamlFile(
|
|
235
|
-
**{
|
|
236
|
-
"db": asdict(self.postgress_access),
|
|
237
|
-
"users": [user for user in self.users],
|
|
238
|
-
"django": {
|
|
239
|
-
"admin": asdict(self.admin_access),
|
|
240
|
-
"debug": True,
|
|
241
|
-
"hosts": ["*"],
|
|
242
|
-
"secret_key": self.secret_key,
|
|
243
|
-
},
|
|
244
|
-
"redis": asdict(self.redis_access),
|
|
245
|
-
"lok": asdict(self.retrieve_credentials()),
|
|
246
|
-
"private_key": self.private_key,
|
|
247
|
-
"public_key": self.public_key,
|
|
248
|
-
"scopes": self.scopes,
|
|
249
|
-
"redeem_tokens": [
|
|
250
|
-
{"user": name, "token": token}
|
|
251
|
-
for name, token in self.registered_tokens.items()
|
|
252
|
-
],
|
|
253
|
-
"groups": [group for group in self.groups],
|
|
254
|
-
"deployment": {"name": self.deployment_name},
|
|
255
|
-
"s3": asdict(self.s3_access),
|
|
256
|
-
"token_expire_seconds": self.token_expiry_seconds,
|
|
257
|
-
"force_script_name": "lok",
|
|
258
|
-
"csrf_trusted_origins": trusted_origins,
|
|
259
|
-
}
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
context.file_tree.set_nested("configs", "lok.yaml", configuration)
|
|
263
|
-
|
|
264
|
-
context.docker_compose.set_nested("services", self.host, db_service)
|
|
265
|
-
|
|
266
|
-
def get_options(self):
|
|
267
|
-
key = rsa.generate_private_key(
|
|
268
|
-
public_exponent=65537, key_size=2048, backend=crypto_default_backend()
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
private_key = key.private_bytes(
|
|
272
|
-
crypto_serialization.Encoding.PEM,
|
|
273
|
-
crypto_serialization.PrivateFormat.PKCS8,
|
|
274
|
-
crypto_serialization.NoEncryption(),
|
|
275
|
-
).decode()
|
|
276
|
-
|
|
277
|
-
public_key = (
|
|
278
|
-
key.public_key()
|
|
279
|
-
.public_bytes(
|
|
280
|
-
crypto_serialization.Encoding.OpenSSH,
|
|
281
|
-
crypto_serialization.PublicFormat.OpenSSH,
|
|
282
|
-
)
|
|
283
|
-
.decode()
|
|
284
|
-
)
|
|
285
|
-
|
|
286
|
-
with_dev = Option(
|
|
287
|
-
subcommand="dev",
|
|
288
|
-
help="Run the service in development mode",
|
|
289
|
-
type=bool,
|
|
290
|
-
default=self.dev,
|
|
291
|
-
show_default=True,
|
|
292
|
-
)
|
|
293
|
-
|
|
294
|
-
with_fakts_url = Option(
|
|
295
|
-
subcommand="db_name",
|
|
296
|
-
help="The name of the database",
|
|
297
|
-
default="db_name",
|
|
298
|
-
show_default=True,
|
|
299
|
-
)
|
|
300
|
-
with_users = Option(
|
|
301
|
-
subcommand="users",
|
|
302
|
-
help="Users that should be greated by default. Format is name:password",
|
|
303
|
-
default=["admin:admin"],
|
|
304
|
-
multiple=True,
|
|
305
|
-
type=USER,
|
|
306
|
-
show_default=True,
|
|
307
|
-
)
|
|
308
|
-
with_groups = Option(
|
|
309
|
-
subcommand="groups",
|
|
310
|
-
help="Groups that should be greated by default. Format is name:description",
|
|
311
|
-
default=["admin:admin_group"],
|
|
312
|
-
multiple=True,
|
|
313
|
-
type=GROUP,
|
|
314
|
-
show_default=True,
|
|
315
|
-
)
|
|
316
|
-
with_public_key = Option(
|
|
317
|
-
subcommand="public_key",
|
|
318
|
-
help="The public key for the JWT creation",
|
|
319
|
-
default=public_key,
|
|
320
|
-
required=True,
|
|
321
|
-
callback=validate_public_key,
|
|
322
|
-
)
|
|
323
|
-
with_private_key = Option(
|
|
324
|
-
subcommand="private_key",
|
|
325
|
-
help="The corresponding private key for the JWT creation",
|
|
326
|
-
default=private_key,
|
|
327
|
-
callback=validate_private_key,
|
|
328
|
-
required=True,
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
return [
|
|
332
|
-
with_dev,
|
|
333
|
-
with_fakts_url,
|
|
334
|
-
with_users,
|
|
335
|
-
with_groups,
|
|
336
|
-
with_private_key,
|
|
337
|
-
with_public_key,
|
|
338
|
-
]
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
def validate_public_key(ctx, param, value):
|
|
342
|
-
if not value.startswith("ssh-rsa"):
|
|
343
|
-
raise click.BadParameter(
|
|
344
|
-
f"Public key must be in ssh-rsa format. Started with {value}"
|
|
345
|
-
)
|
|
346
|
-
return value
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
def validate_private_key(ctx, param, value):
|
|
350
|
-
if not value.startswith("-----BEGIN PRIVATE KEY-----"):
|
|
351
|
-
raise click.BadParameter(
|
|
352
|
-
f"Private key must be in PEM format. Started with {value}"
|
|
353
|
-
)
|
|
354
|
-
return value
|