35c9d90c8aa63a2d49c7784bf5d7d382c7329c67
[strongswan.git] / src / medsrv / controller / user_controller.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Copyright (C) 2008 Philip Boetschi, Adrian Doerig
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18 #include <string.h>
19
20 #include "user_controller.h"
21
22 #include <library.h>
23
24 typedef struct private_user_controller_t private_user_controller_t;
25
26 /**
27 * private data of the user_controller
28 */
29 struct private_user_controller_t {
30
31 /**
32 * public functions
33 */
34 user_controller_t public;
35
36 /**
37 * database connection
38 */
39 database_t *db;
40
41 /**
42 * user session
43 */
44 user_t *user;
45
46 /**
47 * minimum required password length
48 */
49 u_int password_length;
50 };
51
52 /**
53 * hash the password for database storage
54 */
55 static chunk_t hash_password(char *login, char *password)
56 {
57 hasher_t *hasher;
58 chunk_t hash, data;
59
60 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
61 if (!hasher)
62 {
63 return chunk_empty;
64 }
65 data = chunk_cata("cc", chunk_create(login, strlen(login)),
66 chunk_create(password, strlen(password)));
67 if (!hasher->allocate_hash(hasher, data, &hash))
68 {
69 hasher->destroy(hasher);
70 return chunk_empty;
71 }
72 hasher->destroy(hasher);
73 return hash;
74 }
75
76 /**
77 * Login a user.
78 */
79 static void login(private_user_controller_t *this, request_t *request)
80 {
81 if (request->get_query_data(request, "submit"))
82 {
83 char *login, *password;
84
85 login = request->get_query_data(request, "login");
86 password = request->get_query_data(request, "password");
87
88 if (login && password)
89 {
90 enumerator_t *query;
91 u_int id = 0;
92 chunk_t hash;
93
94 hash = hash_password(login, password);
95 query = this->db->query(this->db,
96 "SELECT id FROM user WHERE login = ? AND password = ?",
97 DB_TEXT, login, DB_BLOB, hash, DB_UINT);
98 if (query)
99 {
100 query->enumerate(query, &id);
101 query->destroy(query);
102 }
103 free(hash.ptr);
104 if (id)
105 {
106 this->user->set_user(this->user, id);
107 return request->redirect(request, "peer/list");
108 }
109 }
110 request->setf(request, "error=Invalid username or password.");
111 }
112 request->render(request, "templates/user/login.cs");
113 }
114
115 /**
116 * Logout a user.
117 */
118 static void logout(private_user_controller_t *this, request_t *request)
119 {
120 request->redirect(request, "user/login");
121 request->close_session(request);
122 }
123
124 /**
125 * verify a user entered username for validity
126 */
127 static bool verify_login(private_user_controller_t *this, request_t *request,
128 char *login)
129 {
130 if (!login || *login == '\0')
131 {
132 request->setf(request, "error=Username is missing.");
133 return FALSE;
134 }
135 while (*login != '\0')
136 {
137 switch (*login)
138 {
139 case 'a' ... 'z':
140 case 'A' ... 'Z':
141 case '0' ... '9':
142 case '-':
143 case '_':
144 case '@':
145 case '.':
146 login++;
147 continue;
148 default:
149 request->setf(request, "error=Username invalid, "
150 "valid characters: A-Z a-z 0-9 - _ @ .");
151 }
152 }
153 return TRUE;
154 }
155
156 /**
157 * verify a user entered password for validity
158 */
159 static bool verify_password(private_user_controller_t *this, request_t *request,
160 char *password, char *confirm)
161 {
162 if (!password || *password == '\0')
163 {
164 request->setf(request, "error=Password is missing.");
165 return FALSE;
166 }
167 if (strlen(password) < this->password_length)
168 {
169 request->setf(request, "error=Password requires at least %d characters.",
170 this->password_length);
171 return FALSE;
172 }
173 if (!confirm || !streq(password, confirm))
174 {
175 request->setf(request, "error=Password not confirmed.");
176 return FALSE;
177 }
178 return TRUE;
179 }
180
181 /**
182 * Register a user.
183 */
184 static void add(private_user_controller_t *this, request_t *request)
185 {
186 char *login = "";
187
188 while (request->get_query_data(request, "register"))
189 {
190 char *password, *confirm;
191 chunk_t hash;
192 u_int id;
193
194 login = request->get_query_data(request, "new_login");
195 password = request->get_query_data(request, "new_password");
196 confirm = request->get_query_data(request, "confirm_password");
197
198 if (!verify_login(this, request, login) ||
199 !verify_password(this, request, password, confirm))
200 {
201 break;
202 }
203
204 hash = hash_password(login, password);
205 if (!hash.ptr || this->db->execute(this->db, &id,
206 "INSERT INTO user (login, password) VALUES (?, ?)",
207 DB_TEXT, login, DB_BLOB, hash) < 0)
208 {
209 request->setf(request, "error=Username already exists.");
210 free(hash.ptr);
211 break;
212 }
213 free(hash.ptr);
214 this->user->set_user(this->user, id);
215 return request->redirect(request, "peer/list");
216 }
217 request->set(request, "new_login", login);
218 request->setf(request, "password_length=%d", this->password_length);
219 request->render(request, "templates/user/add.cs");
220 }
221
222 /**
223 * Edit the logged in user
224 */
225 static void edit(private_user_controller_t *this, request_t *request)
226 {
227 enumerator_t *query;
228 char *old_login;
229
230 /* lookup old login */
231 query = this->db->query(this->db, "SELECT login FROM user WHERE id = ?",
232 DB_INT, this->user->get_user(this->user),
233 DB_TEXT);
234 if (!query || !query->enumerate(query, &old_login))
235 {
236 DESTROY_IF(query);
237 request->close_session(request);
238 return request->redirect(request, "user/login");
239 }
240 old_login = strdupa(old_login);
241 query->destroy(query);
242
243 /* back pressed */
244 if (request->get_query_data(request, "back"))
245 {
246 return request->redirect(request, "peer/list");
247 }
248 /* delete pressed */
249 if (request->get_query_data(request, "delete"))
250 {
251 this->db->execute(this->db, NULL, "DELETE FROM user WHERE id = ?",
252 DB_UINT, this->user->get_user(this->user));
253 this->db->execute(this->db, NULL,
254 "DELETE FROM peer WHERE user = ?",
255 DB_UINT, this->user->get_user(this->user));
256 return logout(this, request);
257 }
258 /* save pressed */
259 while (request->get_query_data(request, "save"))
260 {
261 char *new_login, *old_pass, *new_pass, *confirm;
262 chunk_t old_hash, new_hash;
263
264 new_login = request->get_query_data(request, "old_login");
265 old_pass = request->get_query_data(request, "old_password");
266 new_pass = request->get_query_data(request, "new_password");
267 confirm = request->get_query_data(request, "confirm_password");
268
269 if (!verify_login(this, request, new_login) ||
270 !verify_password(this, request, new_pass, confirm))
271 {
272 old_login = new_login;
273 break;
274 }
275 old_hash = hash_password(old_login, old_pass);
276 new_hash = hash_password(new_login, new_pass);
277
278 if (this->db->execute(this->db, NULL,
279 "UPDATE user SET login = ?, password = ? "
280 "WHERE id = ? AND password = ?",
281 DB_TEXT, new_login, DB_BLOB, new_hash,
282 DB_UINT, this->user->get_user(this->user), DB_BLOB, old_hash) <= 0)
283 {
284 free(new_hash.ptr);
285 free(old_hash.ptr);
286 old_login = new_login;
287 request->setf(request, "error=Password verification failed.");
288 break;
289 }
290 free(new_hash.ptr);
291 free(old_hash.ptr);
292 return request->redirect(request, "peer/list");
293 }
294 /* on error/template rendering */
295 request->set(request, "old_login", old_login);
296 request->setf(request, "password_length=%d", this->password_length);
297 request->render(request, "templates/user/edit.cs");
298 }
299
300 METHOD(controller_t, get_name, char*,
301 private_user_controller_t *this)
302 {
303 return "user";
304 }
305
306 METHOD(controller_t, handle, void,
307 private_user_controller_t *this, request_t *request, char *action,
308 char *p2, char *p3, char *p4, char *p5)
309 {
310 if (action)
311 {
312 if (streq(action, "add"))
313 {
314 return add(this, request);
315 }
316 if (streq(action, "login"))
317 {
318 return login(this, request);
319 }
320 else if (streq(action, "logout"))
321 {
322 return logout(this, request);
323 }
324 else if (streq(action, "edit"))
325 {
326 return edit(this, request);
327 }
328 else if (streq(action, "help"))
329 {
330 return request->render(request, "templates/user/help.cs");
331 }
332 }
333 request->redirect(request, "user/login");
334 }
335
336 METHOD(controller_t, destroy, void,
337 private_user_controller_t *this)
338 {
339 free(this);
340 }
341
342 /*
343 * see header file
344 */
345 controller_t *user_controller_create(user_t *user, database_t *db)
346 {
347 private_user_controller_t *this;
348
349 INIT(this,
350 .public = {
351 .controller = {
352 .get_name = _get_name,
353 .handle = _handle,
354 .destroy = _destroy,
355 },
356 },
357 .user = user,
358 .db = db,
359 .password_length = lib->settings->get_int(lib->settings,
360 "medsrv.password_length", 6),
361 );
362
363 return &this->public.controller;
364 }
365