]> git.immae.eu Git - perso/Immae/Config/Nix.git/blob - systems/eldiron/websites/tools/landing/ldap_ssh_keys.php
Add config for CI
[perso/Immae/Config/Nix.git] / systems / eldiron / websites / tools / landing / ldap_ssh_keys.php
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
3 <head>
4 <title>ImmaeEu Account</title>
5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
6 <meta name="viewport" content="width=device-width, initial-scale=1" />
7 <link rel="stylesheet" href="https://assets.immae.eu/skeleton/2.0.4/skeleton.min.css" integrity="sha256-2YQRJMXD7pIAPHiXr0s+vlRWA7GYJEK0ARns7k2sbHY=" crossorigin="anonymous" />
8 <style type="text/css">
9 body {
10 font-family: Verdana,Arial,Courier New;
11 margin: auto;
12 }
13 table#ssh_keys_list textarea {
14 width: 100%;
15 height: 100%;
16 }
17 table#ssh_keys_list tbody tr.sshkeyrow {
18 height: 130px;
19 }
20 table#ssh_keys_list tbody tr.headrow th {
21 border-bottom: 0px !important;
22 padding-bottom: 0px !important;
23 }
24 table#ssh_keys_list tbody tr.mainrow td:not(.delete-button) {
25 border-bottom: 0px !important;
26 padding-bottom: 0px !important;
27 }
28 table#ssh_keys_list td.sshkey {
29 min-width: 600px;
30 height: 100%;
31 padding-top: 0px !important;
32 }
33
34 table#ssh_keys_list td.comment {
35 min-width: 160px;
36 }
37
38 </style>
39 </head>
40 <body>
41 <div class="container">
42 <h1>Gestion des clés SSH</h1>
43 <?php
44
45 $connection = NULL;
46
47 session_start();
48
49 // Liste des applications gérées
50 const apps = [
51 'git',
52 'pub',
53 'ftp',
54 'ssh',
55 'forward',
56 ];
57
58 function checkSshKey($sshKey)
59 {
60 $exploded = explode(' ', $sshKey);
61 if (count($exploded) != 2) {
62 return false;
63 }
64 if (!in_array($exploded[0], array('ssh-rsa', 'ssh-ed25519'))) {
65 return false;
66 }
67 $decoded = base64_decode($exploded[1], true);
68 if ($decoded === FALSE) {
69 return false;
70 }
71 $decoded = preg_replace("/[^\w\-]/","", (string) $decoded);
72 if (substr($decoded, 0, strlen($exploded[0])) !== $exploded[0]) {
73 return false;
74 }
75
76 return true;
77 }
78
79 function isUserLogged()
80 {
81 return (isset($_SESSION["login"]));
82 }
83
84 function checkLogin($user, $password)
85 {
86 $server = "ldaps://ldap.immae.eu";
87 $con = ldap_connect($server);
88 ldap_set_option($con, LDAP_OPT_PROTOCOL_VERSION, 3);
89
90 $user_dn = "uid=$user,ou=users,dc=immae,dc=eu";
91
92 if (ldap_bind($con, $user_dn, $password) === false) {
93 return false;
94 }
95 $_SESSION["user_dn"] = $user_dn;
96
97 $user_search = ldap_search($con,"dc=immae,dc=eu","(uid=$user)");
98 $auth_entry = ldap_first_entry($con, $user_search);
99
100 return true;
101 }
102
103 function connectPg() {
104 foreach(["PGUSER", "PGPASSWORD", "PGDATABASE", "PGHOST"] as $k) {
105 if (isset($_SERVER[$k]) && !isset($_ENV[$k])) {
106 putenv("${k}=" . $_SERVER[$k]);
107 }
108 }
109 $con = pg_connect("");
110 if (!$con) {
111 die("database access error");
112 }
113 return $con;
114 }
115
116 function getKeys()
117 {
118 $keys = [];
119 if (!isset($_SESSION["login"]))
120 return $keys;
121 $pg = connectPg();
122 $result = pg_query_params($pg, "SELECT id,key,array_to_json(usage) as usage,comment FROM ldap_users_ssh_keys WHERE realm = 'immae' AND login = $1 ORDER BY id", array($_SESSION["login"]));
123 if (!$result) {
124 die("database access error");
125 }
126 $keys = [];
127 while ($row = pg_fetch_assoc($result)) {
128 $keys[] = array(
129 'id' => $row["id"],
130 'apps' => json_decode($row["usage"]),
131 'public_key' => $row["key"],
132 'comment' => $row["comment"],
133 );
134 }
135
136 pg_close($pg);
137 return $keys;
138 }
139
140 function saveKeys($keys)
141 {
142 if (!isset($_SESSION["login"])) {
143 return false;
144 }
145 $pg = connectPg();
146 $existingIds = pg_fetch_all_columns(pg_query_params($pg, "SELECT id FROM ldap_users_ssh_keys WHERE realm = 'immae' AND login = $1", array($_SESSION["login"])));
147 foreach ($keys as $key) {
148 if (isset($key["id"])) {
149 unset($existingIds[array_search($key["id"],$existingIds)]);
150 pg_query_params($pg, "UPDATE ldap_users_ssh_keys SET key = $2, usage = ARRAY(SELECT * FROM json_array_elements_text($3))::ldap_users_ssh_key_usage[], comment = $4 WHERE id = $5 AND login = $1 AND realm = 'immae'", array($_SESSION["login"], $key["public_key"], json_encode($key["apps"]), $key["comment"], $key["id"]));
151 } else {
152 pg_query_params($pg, "INSERT INTO ldap_users_ssh_keys (login,realm,key,usage,comment) values ($1,'immae',$2,ARRAY(SELECT * FROM json_array_elements_text($3))::ldap_users_ssh_key_usage[],$4)", array($_SESSION["login"], $key["public_key"], json_encode($key["apps"]), $key["comment"]));
153 }
154 }
155 foreach ($existingIds as $removedKeyId) {
156 pg_query_params($pg, "DELETE FROM ldap_users_ssh_keys WHERE login = $1 AND realm = 'immae' AND id = $2", array($_SESSION["login"], $removedKeyId));
157 }
158 }
159
160
161 // Script
162 if (isset($_POST['deconnexion'])) {
163 $_SESSION = [];
164 }
165
166 if (isset($_POST['sauvegarder'])) {
167 $editedKeys = [];
168 $errors = false;
169 $keysToSave = [];
170 foreach($_POST['keys'] as $id => $key) {
171 $editedKeys[$id] = $key;
172 if (!checkSshKey($key['public_key'])) {
173 $editedKeys[$id]['error'] = true;
174 $errors = true;
175 }
176
177 if (!isset($key['apps'])) {
178 $editedKeys[$id]['apps'] = $key['apps'] = [];
179
180 }
181 foreach ($key['apps'] as $app) {
182 if (!in_array($app, apps)) {
183 die("integrity");
184 }
185 }
186
187 if (!isset($editedKeys[$id]['error']) || $editedKeys[$id]['error'] !== true) {
188 $keysToSave[] = $key;
189 }
190 }
191
192 if (!$errors) {
193 $successSave = saveKeys($keysToSave);
194 }
195 }
196
197 $loginErrors = "";
198 if (isset($_POST['login'])) {
199 if (empty($_POST['username']) || empty($_POST['password'])) {
200 $loginErrors = "Le nom d'utilisateur et le mot de passe sont requis.";
201 } elseif (!checkLogin($_POST['username'], $_POST['password'])) {
202 $loginErrors = "Identifiants incorrects.";
203 } else {
204 $_SESSION['login'] = $_POST['username'];
205 }
206 }
207
208 if (isUserLogged()) :
209 $keys = isset($editedKeys) ? $editedKeys : getKeys();
210 ?>
211 <p>Connecté en tant que <b><?= $_SESSION['login']; ?></b></p>
212
213 <form method="post">
214 <input type="submit" name="deconnexion" value="Déconnexion">
215 </form>
216
217 <?php if (isset($successSave) && $successSave === true) : ?>
218 <p style="color: green;">Clés enregistrées avec succès.</p>
219 <?php endif; ?>
220
221 <form method="post">
222 <table id="ssh_keys_list">
223 <tbody>
224 <?php
225 foreach ($keys as $id => $sshKey) :
226 ?>
227 <tr class="headrow">
228 <th>Description</th>
229 <?php foreach (apps as $app) : ?>
230 <th><?= $app ?></th>
231 <?php endforeach; ?>
232 <th></th>
233 </tr>
234 <tr class="mainrow">
235 <td class="comment"><textarea name="keys[<?= $id ?>][comment]"><?= $sshKey['comment'] ?></textarea></td>
236 <?php
237 foreach (apps as $app) :
238 $checked = in_array($app, $sshKey['apps']);
239 ?>
240 <td><input type="checkbox" name="keys[<?= $id ?>][apps][]" value="<?= $app ?>"<?= $checked ? ' checked' : '' ?>></td>
241 <?php endforeach; ?>
242 <td class="delete-button" rowspan="2"><input type="hidden" name="keys[<?= $id ?>][id]" value="<?= $sshKey["id"] ?>"><button class="delete">Suppr.</button></td>
243 </tr>
244 <tr class="sshkeyrow">
245 <td colspan="<?php echo 1+count(apps); ?>" class="sshkey"><textarea name="keys[<?= $id ?>][public_key]" <?php if (isset($sshKey['error']) && $sshKey['error'] === true) :?>style="color: red"<?php endif; ?>><?= $sshKey['public_key'] ?></textarea></td>
246 </tr>
247 <?php
248 endforeach;
249 ?>
250 </tbody>
251 </table>
252
253 <button id="add">Ajouter</button>
254
255 <hr>
256
257 <input type="submit" value="Sauvegarder" name="sauvegarder">
258 </form>
259 <script>
260 function deleteLine(element) {
261 element.addEventListener('click', function(e) {
262 e.preventDefault();
263 e.target.closest('tr').nextElementSibling.remove();
264 e.target.closest('tr').previousElementSibling.remove();
265 e.target.closest('tr').remove();
266 }, false);
267 }
268
269 var suppr = document.getElementsByClassName('delete');
270 var add = document.getElementById('add');
271 var list = document.querySelector('#ssh_keys_list > tbody');
272
273 for (var i = 0; i < suppr.length; i++) {
274 deleteLine(suppr[i]);
275 }
276
277 add.addEventListener('click', function (e) {
278 e.preventDefault();
279 i++;
280
281 var newLine = `
282 <tr class="headrow">
283 <th>Description</th>
284 <?php foreach (apps as $app) : ?>
285 <th><?= $app ?></th>
286 <?php endforeach; ?>
287 <th></th>
288 </tr>
289 <tr class="mainrow">
290 <td class="comment"><textarea name="keys[${i}][comment]"></textarea></td>
291 `;
292
293
294 <?php
295 foreach (apps as $app) :
296 ?>
297 newLine += `<td><input type="checkbox" name="keys[${i}][apps][]" value="<?= $app ?>"></td>`;
298 <?php endforeach; ?>
299
300 newLine += `<td class="delete-button" rowspan="2"><button class="delete" id="delete-${i}">Suppr.</button></td>
301 </tr>`;
302
303 newLine += `<tr class="sshkeyrow">
304 <td colspan="<?php echo 1+count(apps); ?>" class="sshkey"><textarea name="keys[${i}][public_key]"></textarea></td>
305 </tr>`;
306
307
308 list.insertAdjacentHTML('beforeend', newLine);
309
310 deleteLine(document.getElementById("delete-" + i));
311
312 }, false)
313 </script>
314 <?php
315 else:
316 ?>
317 <form action="" method="post">
318 <h2>Login</h2>
319
320 <?php
321 if (!empty($loginErrors)):
322 ?>
323 <p style="color: red;"><?= $loginErrors; ?></p>
324 <?php
325 endif;
326 ?>
327
328 <label for="username">Utilisateur :</label>
329 <input type="text" id="username" name="username"/>
330
331 <label for="password">Mot de passe :</label>
332 <input type="password" id="password" name="password"/>
333
334 <input type="submit" value="OK" name="login" />
335 </form>
336 <?php
337 endif;
338 ?>
339 </div>
340 </body>
341 </html>
342
343