aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThibault Pelloquin <thibault.pelloquin@gmail.com>2021-10-19 23:32:45 +0200
committerIsmaël Bouya <ismael.bouya@normalesup.org>2021-10-20 20:13:34 +0200
commit65da25ba402a308f72dd3692335fbffbe1409f56 (patch)
tree6eceed69d4e0a70caa5e5b968efd884f9f07814c
parent668ba4d439d3b60ade9f96fd158ed293ff211f60 (diff)
downloadNix-65da25ba402a308f72dd3692335fbffbe1409f56.tar.gz
Nix-65da25ba402a308f72dd3692335fbffbe1409f56.tar.zst
Nix-65da25ba402a308f72dd3692335fbffbe1409f56.zip
Add script to handle ssh keys
Work by Thibault
-rw-r--r--modules/private/websites/tools/tools/landing/ldap_ssh_keys.php348
-rw-r--r--nix/sources.json2
m---------nixops/secrets0
3 files changed, 349 insertions, 1 deletions
diff --git a/modules/private/websites/tools/tools/landing/ldap_ssh_keys.php b/modules/private/websites/tools/tools/landing/ldap_ssh_keys.php
new file mode 100644
index 0000000..259e28d
--- /dev/null
+++ b/modules/private/websites/tools/tools/landing/ldap_ssh_keys.php
@@ -0,0 +1,348 @@
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
47session_start();
48
49// Liste des applications gérées
50const apps = [
51 'git',
52 'pub',
53 'ftp',
54 'ssh',
55 'forward',
56];
57
58function 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
79function isUserLogged()
80{
81 return (isset($_SESSION["login"]) && doConnect() !== NULL);
82}
83
84function doConnect()
85{
86 global $connection;
87 $server = "ldaps://ldap.immae.eu";
88
89 if ($connection === NULL) {
90 $connection = ldap_connect($server);
91 ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, 3);
92 if (isset($_SESSION["user_dn"]) && isset($_SESSION["password"])) {
93 if (ldap_bind($connection, $_SESSION["user_dn"], $_SESSION["password"]) === false) {
94 $connection = NULL;
95 unset($_SESSION["user_dn"]);
96 unset($_SESSION["password"]);
97 unset($_SESSION["login"]);
98 }
99 }
100 }
101
102 return $connection;
103}
104
105function checkLogin($user, $password)
106{
107 $con = doConnect();
108
109 $user_dn = "uid=$user,ou=users,dc=immae,dc=eu";
110
111 if (ldap_bind($con, $user_dn, $password) === false) {
112 return false;
113 }
114 $_SESSION["user_dn"] = $user_dn;
115 $_SESSION["password"] = $password;
116
117 $user_search = ldap_search($con,"dc=immae,dc=eu","(uid=$user)");
118 $auth_entry = ldap_first_entry($con, $user_search);
119
120 return true;
121}
122
123function getLdapInfo()
124{
125 $con = doConnect();
126 if (!isset($_SESSION["user_dn"])) {
127 $sortieLdap = [];
128 } else {
129 $user_read = ldap_read($con, $_SESSION["user_dn"], "(objectclass=*)", array("uid","immaeSshKey"));
130 $user_entry = ldap_first_entry($con, $user_read);
131 $sortieLdap = ldap_get_values($con, $user_entry, "immaeSshKey");
132 unset($sortieLdap["count"]);
133 }
134
135 $keys = [];
136 foreach ($sortieLdap as $line) {
137 $exploded = explode(' ', $line);
138
139 $apps = explode('|', $exploded[0]);
140 $publicKey = $exploded[1] . ' ' . $exploded[2];
141
142 unset($exploded[0]);
143 unset($exploded[1]);
144 unset($exploded[2]);
145
146 $comment = implode(' ', $exploded);
147
148 $keys[] = [
149 'apps' => $apps,
150 'public_key' => $publicKey,
151 'comment' => $comment,
152 ];
153 }
154
155 return $keys;
156}
157
158function pushLdapInfos($keys)
159{
160 $con = doConnect();
161 if (!isset($_SESSION["user_dn"]))
162 return false;
163
164 return ldap_mod_replace($con, $_SESSION["user_dn"], array("immaeSshKey" => $keys));
165}
166
167
168// Script
169if (isset($_POST['deconnexion'])) {
170 $_SESSION = [];
171}
172
173if (isset($_POST['sauvegarder'])) {
174 $editedKeys = [];
175 $errors = false;
176 $keysToSave = [];
177 foreach($_POST['keys'] as $id => $key) {
178 $editedKeys[$id] = $key;
179 if (!checkSshKey($key['public_key'])) {
180 $editedKeys[$id]['error'] = true;
181 $errors = true;
182 }
183
184 if (!isset($key['apps'])) {
185 $editedKeys[$id]['apps'] = $key['apps'] = [];
186
187 }
188 foreach ($key['apps'] as $app) {
189 if (!in_array($app, apps)) {
190 die("integrity");
191 }
192 }
193
194 if (!isset($editedKeys[$id]['error']) || $editedKeys[$id]['error'] !== true) {
195 $keysToSave[] = implode('|', $key['apps']) . ' ' . $key['public_key'] . ' ' . $key['comment'];
196 }
197 }
198
199 if (!$errors) {
200 $successSave = pushLdapInfos($keysToSave);
201 }
202}
203
204$loginErrors = "";
205if (isset($_POST['login'])) {
206 if (empty($_POST['username']) || empty($_POST['password'])) {
207 $loginErrors = "Le nom d'utilisateur et le mot de passe sont requis.";
208 } elseif (!checkLogin($_POST['username'], $_POST['password'])) {
209 $loginErrors = "Identifiants incorrects.";
210 } else {
211 $_SESSION['login'] = $_POST['username'];
212 }
213}
214
215if (isUserLogged()) :
216 $keys = isset($editedKeys) ? $editedKeys : getLdapInfo();
217?>
218 <p>Connecté en tant que <b><?= $_SESSION['login']; ?></b></p>
219
220 <form method="post">
221 <input type="submit" name="deconnexion" value="Déconnexion">
222 </form>
223
224 <?php if (isset($successSave) && $successSave === true) : ?>
225 <p style="color: green;">Clés enregistrées avec succès.</p>
226 <?php endif; ?>
227
228 <form method="post">
229 <table id="ssh_keys_list">
230 <tbody>
231 <?php
232 foreach ($keys as $id => $sshKey) :
233 ?>
234 <tr class="headrow">
235 <th>Description</th>
236 <?php foreach (apps as $app) : ?>
237 <th><?= $app ?></th>
238 <?php endforeach; ?>
239 <th></th>
240 </tr>
241 <tr class="mainrow">
242 <td class="comment"><textarea name="keys[<?= $id ?>][comment]"><?= $sshKey['comment'] ?></textarea></td>
243 <?php
244 foreach (apps as $app) :
245 $checked = in_array($app, $sshKey['apps']);
246 ?>
247 <td><input type="checkbox" name="keys[<?= $id ?>][apps][]" value="<?= $app ?>"<?= $checked ? ' checked' : '' ?>></td>
248 <?php endforeach; ?>
249 <td class="delete-button" rowspan="2"><button class="delete">Suppr.</button></td>
250 </tr>
251 <tr class="sshkeyrow">
252 <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>
253 </tr>
254 <?php
255 endforeach;
256 ?>
257 </tbody>
258 </table>
259
260 <button id="add">Ajouter</button>
261
262 <hr>
263
264 <input type="submit" value="Sauvegarder" name="sauvegarder">
265 </form>
266 <script>
267 function deleteLine(element) {
268 element.addEventListener('click', function(e) {
269 e.preventDefault();
270 e.target.closest('tr').remove();
271 }, false);
272 }
273
274 var suppr = document.getElementsByClassName('delete');
275 var add = document.getElementById('add');
276 var list = document.querySelector('#ssh_keys_list > tbody');
277
278 for (var i = 0; i < suppr.length; i++) {
279 deleteLine(suppr[i]);
280 }
281
282 add.addEventListener('click', function (e) {
283 e.preventDefault();
284 i++;
285
286 var newLine = `
287 <tr class="headrow">
288 <th>Description</th>
289 <?php foreach (apps as $app) : ?>
290 <th><?= $app ?></th>
291 <?php endforeach; ?>
292 <th></th>
293 </tr>
294 <tr class="mainrow">
295 <td class="comment"><textarea name="keys[${i}][comment]"></textarea></td>
296 `;
297
298
299 <?php
300 foreach (apps as $app) :
301 ?>
302 newLine += `<td><input type="checkbox" name="keys[${i}][apps][]" value="<?= $app ?>"></td>`;
303 <?php endforeach; ?>
304
305 newLine += `<td class="delete-button" rowspan="2"><button class="delete" id="delete-${i}">Suppr.</button></td>
306 </tr>`;
307
308 newLine += `<tr class="sshkeyrow">
309 <td colspan="<?php echo 1+count(apps); ?>" class="sshkey"><textarea name="keys[$[i}][public_key]"></textarea></td>
310 </tr>`;
311
312
313 list.insertAdjacentHTML('beforeend', newLine);
314
315 deleteLine(document.getElementById("delete-" + i));
316
317 }, false)
318 </script>
319<?php
320else:
321?>
322 <form action="" method="post">
323 <h2>Login</h2>
324
325 <?php
326 if (!empty($loginErrors)):
327 ?>
328 <p style="color: red;"><?= $loginErrors; ?></p>
329 <?php
330 endif;
331 ?>
332
333 <label for="username">Utilisateur :</label>
334 <input type="text" id="username" name="username"/>
335
336 <label for="password">Mot de passe :</label>
337 <input type="password" id="password" name="password"/>
338
339 <input type="submit" value="OK" name="login" />
340 </form>
341<?php
342endif;
343?>
344 </div>
345 </body>
346</html>
347
348
diff --git a/nix/sources.json b/nix/sources.json
index 961d378..1a1cf5a 100644
--- a/nix/sources.json
+++ b/nix/sources.json
@@ -132,7 +132,7 @@
132 "webapps-landing": { 132 "webapps-landing": {
133 "ref": "gitolite_local/local_changes", 133 "ref": "gitolite_local/local_changes",
134 "repo": "https://git.immae.eu/github/bastienwirtz/homer.git", 134 "repo": "https://git.immae.eu/github/bastienwirtz/homer.git",
135 "rev": "49b6104e9d0059b7990b3dcd53cca664d5cce7af", 135 "rev": "bb60c5b869931f305f15c5bfa9cdb3f68702f01f",
136 "type": "git", 136 "type": "git",
137 "version": "e0a72b7-local" 137 "version": "e0a72b7-local"
138 }, 138 },
diff --git a/nixops/secrets b/nixops/secrets
Subproject 0b9f489a7e2e01208d4285c26348b4fa09607e1 Subproject 39f3c2c33c57df2a502b3cdf45635d0afe27273