1{ lib, ... }:
2let
3 domain = "meet.local";
4 oidcDomain = "127.0.0.1:8080";
5in
6
7{
8 name = "lasuite-meet";
9 meta.maintainers = with lib.maintainers; [ soyouzpanda ];
10
11 nodes.machine =
12 { pkgs, ... }:
13 {
14 virtualisation.memorySize = 4 * 1024;
15
16 networking.hosts."127.0.0.1" = [ domain ];
17
18 environment.systemPackages = with pkgs; [ jq ];
19
20 services.lasuite-meet = {
21 enable = true;
22 enableNginx = true;
23 livekit = {
24 enable = true;
25 keyFile = pkgs.writeText "lasuite-meet-livekit-keys" ''
26 lasuite-meet: ca50qKzxEXVIu61wHshAyJNzlWw8vlIwUuzxQbUK1rG
27 '';
28 };
29 redis.createLocally = true;
30 postgresql.createLocally = true;
31
32 inherit domain;
33
34 settings = {
35 DJANGO_SECRET_KEY_FILE = pkgs.writeText "django-secret-file" ''
36 8540db59c03943d48c3ed1a0f96ce3b560e0f45274f120f7ee4dace3cc366a6b
37 '';
38
39 OIDC_OP_JWKS_ENDPOINT = "http://${oidcDomain}/dex/keys";
40 OIDC_OP_AUTHORIZATION_ENDPOINT = "http://${oidcDomain}/dex/auth/mock";
41 OIDC_OP_TOKEN_ENDPOINT = "http://${oidcDomain}/dex/token";
42 OIDC_OP_USER_ENDPOINT = "http://${oidcDomain}/dex/userinfo";
43 OIDC_RP_CLIENT_ID = "lasuite-meet";
44 OIDC_RP_SIGN_ALGO = "RS256";
45 OIDC_RP_SCOPES = "openid email";
46 OIDC_RP_CLIENT_SECRET = "lasuitemeetclientsecret";
47
48 LOGIN_REDIRECT_URL = "http://${domain}";
49 LOGIN_REDIRECT_URL_FAILURE = "http://${domain}";
50 LOGOUT_REDIRECT_URL = "http://${domain}";
51
52 LIVEKIT_API_KEY = "lasuite-meet";
53 LIVEKIT_API_SECRET = "ca50qKzxEXVIu61wHshAyJNzlWw8vlIwUuzxQbUK1rG";
54
55 # Disable HTTPS feature in tests because we're running on a HTTP connection
56 DJANGO_SECURE_PROXY_SSL_HEADER = "";
57 DJANGO_SECURE_SSL_REDIRECT = false;
58 DJANGO_CSRF_COOKIE_SECURE = false;
59 DJANGO_SESSION_COOKIE_SECURE = false;
60 DJANGO_CSRF_TRUSTED_ORIGINS = "http://*";
61 };
62 };
63
64 services.dex = {
65 enable = true;
66 settings = {
67 issuer = "http://${oidcDomain}/dex";
68 storage = {
69 type = "postgres";
70 config.host = "/var/run/postgresql";
71 };
72 web.http = "127.0.0.1:8080";
73 oauth2.skipApprovalScreen = true;
74 staticClients = [
75 {
76 id = "lasuite-meet";
77 name = "Meet";
78 redirectURIs = [ "http://${domain}/api/v1.0/callback/" ];
79 secretFile = "/etc/dex/lasuite-meet";
80 }
81 ];
82 connectors = [
83 {
84 type = "mockPassword";
85 id = "mock";
86 name = "Example";
87 config = {
88 username = "admin";
89 password = "password";
90 };
91 }
92 ];
93 };
94 };
95
96 environment.etc."dex/lasuite-meet" = {
97 mode = "0400";
98 user = "dex";
99 text = "lasuitemeetclientsecret";
100 };
101
102 services.postgresql = {
103 enable = true;
104 ensureDatabases = [ "dex" ];
105 ensureUsers = [
106 {
107 name = "dex";
108 ensureDBOwnership = true;
109 }
110 ];
111 };
112 };
113
114 testScript = ''
115 with subtest("Wait for units to start"):
116 machine.wait_for_unit("dex.service")
117 machine.wait_for_unit("lasuite-meet.service")
118 machine.wait_for_unit("lasuite-meet-celery.service")
119
120 with subtest("Wait for web servers to start"):
121 machine.wait_until_succeeds("curl -fs 'http://${domain}/api/v1.0/authenticate/'", timeout=120)
122 machine.wait_until_succeeds("curl -fs '${oidcDomain}/dex/auth/mock?client_id=lasuite-meet&response_type=code&redirect_uri=http://${domain}/api/v1.0/callback/&scope=openid'", timeout=120)
123
124 with subtest("Login"):
125 state, nonce = machine.succeed("curl -fs -c cjar 'http://${domain}/api/v1.0/authenticate/' -w '%{redirect_url}' | sed -n 's/.*state=\\(.*\\)&nonce=\\(.*\\)/\\1 \\2/p'").strip().split(' ')
126
127 oidc_state = machine.succeed(f"curl -fs '${oidcDomain}/dex/auth/mock?client_id=lasuite-meet&response_type=code&redirect_uri=http://${domain}/api/v1.0/callback/&scope=openid+email&state={state}&nonce={nonce}' | sed -n 's/.*state=\\(.*\\)\">.*/\\1/p'").strip()
128
129 code = machine.succeed(f"curl -fs '${oidcDomain}/dex/auth/mock/login?back=&state={oidc_state}' -d 'login=admin&password=password' -w '%{{redirect_url}}' | sed -n 's/.*code=\\(.*\\)&.*/\\1/p'").strip()
130 print(f"Got approval code {code}")
131
132 machine.succeed(f"curl -fs -c cjar -b cjar 'http://${domain}/api/v1.0/callback/?code={code}&state={state}'")
133
134 with subtest("Create a room"):
135 csrf_token = machine.succeed("grep csrftoken cjar | cut -f 7 | tr -d '\n'")
136
137 room_id = machine.succeed(f"curl -fs -c cjar -b cjar 'http://${domain}/api/v1.0/rooms/' -X POST -H 'Content-Type: application/json' -H 'X-CSRFToken: {csrf_token}' -H 'Referer: http://${domain}' -d '{{\"name\": \"aaa-bbbb-ccc\"}}' | jq .id -r").strip()
138
139 print(f"Created room with id {room_id}")
140 '';
141}