libfast: add a fast_ prefix to all classes, avoiding namespace clashes
[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, fast_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, fast_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,
128 fast_request_t *request, 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,
160 fast_request_t *request,
161 char *password, char *confirm)
162 {
163 if (!password || *password == '\0')
164 {
165 request->setf(request, "error=Password is missing.");
166 return FALSE;
167 }
168 if (strlen(password) < this->password_length)
169 {
170 request->setf(request, "error=Password requires at least %d characters.",
171 this->password_length);
172 return FALSE;
173 }
174 if (!confirm || !streq(password, confirm))
175 {
176 request->setf(request, "error=Password not confirmed.");
177 return FALSE;
178 }
179 return TRUE;
180 }
181
182 /**
183 * Register a user.
184 */
185 static void add(private_user_controller_t *this, fast_request_t *request)
186 {
187 char *login = "";
188
189 while (request->get_query_data(request, "register"))
190 {
191 char *password, *confirm;
192 chunk_t hash;
193 u_int id;
194
195 login = request->get_query_data(request, "new_login");
196 password = request->get_query_data(request, "new_password");
197 confirm = request->get_query_data(request, "confirm_password");
198
199 if (!verify_login(this, request, login) ||
200 !verify_password(this, request, password, confirm))
201 {
202 break;
203 }
204
205 hash = hash_password(login, password);
206 if (!hash.ptr || this->db->execute(this->db, &id,
207 "INSERT INTO user (login, password) VALUES (?, ?)",
208 DB_TEXT, login, DB_BLOB, hash) < 0)
209 {
210 request->setf(request, "error=Username already exists.");
211 free(hash.ptr);
212 break;
213 }
214 free(hash.ptr);
215 this->user->set_user(this->user, id);
216 return request->redirect(request, "peer/list");
217 }
218 request->set(request, "new_login", login);
219 request->setf(request, "password_length=%d", this->password_length);
220 request->render(request, "templates/user/add.cs");
221 }
222
223 /**
224 * Edit the logged in user
225 */
226 static void edit(private_user_controller_t *this, fast_request_t *request)
227 {
228 enumerator_t *query;
229 char *old_login;
230
231 /* lookup old login */
232 query = this->db->query(this->db, "SELECT login FROM user WHERE id = ?",
233 DB_INT, this->user->get_user(this->user),
234 DB_TEXT);
235 if (!query || !query->enumerate(query, &old_login))
236 {
237 DESTROY_IF(query);
238 request->close_session(request);
239 return request->redirect(request, "user/login");
240 }
241 old_login = strdupa(old_login);
242 query->destroy(query);
243
244 /* back pressed */
245 if (request->get_query_data(request, "back"))
246 {
247 return request->redirect(request, "peer/list");
248 }
249 /* delete pressed */
250 if (request->get_query_data(request, "delete"))
251 {
252 this->db->execute(this->db, NULL, "DELETE FROM user WHERE id = ?",
253 DB_UINT, this->user->get_user(this->user));
254 this->db->execute(this->db, NULL,
255 "DELETE FROM peer WHERE user = ?",
256 DB_UINT, this->user->get_user(this->user));
257 return logout(this, request);
258 }
259 /* save pressed */
260 while (request->get_query_data(request, "save"))
261 {
262 char *new_login, *old_pass, *new_pass, *confirm;
263 chunk_t old_hash, new_hash;
264
265 new_login = request->get_query_data(request, "old_login");
266 old_pass = request->get_query_data(request, "old_password");
267 new_pass = request->get_query_data(request, "new_password");
268 confirm = request->get_query_data(request, "confirm_password");
269
270 if (!verify_login(this, request, new_login) ||
271 !verify_password(this, request, new_pass, confirm))
272 {
273 old_login = new_login;
274 break;
275 }
276 old_hash = hash_password(old_login, old_pass);
277 new_hash = hash_password(new_login, new_pass);
278
279 if (this->db->execute(this->db, NULL,
280 "UPDATE user SET login = ?, password = ? "
281 "WHERE id = ? AND password = ?",
282 DB_TEXT, new_login, DB_BLOB, new_hash,
283 DB_UINT, this->user->get_user(this->user), DB_BLOB, old_hash) <= 0)
284 {
285 free(new_hash.ptr);
286 free(old_hash.ptr);
287 old_login = new_login;
288 request->setf(request, "error=Password verification failed.");
289 break;
290 }
291 free(new_hash.ptr);
292 free(old_hash.ptr);
293 return request->redirect(request, "peer/list");
294 }
295 /* on error/template rendering */
296 request->set(request, "old_login", old_login);
297 request->setf(request, "password_length=%d", this->password_length);
298 request->render(request, "templates/user/edit.cs");
299 }
300
301 METHOD(fast_controller_t, get_name, char*,
302 private_user_controller_t *this)
303 {
304 return "user";
305 }
306
307 METHOD(fast_controller_t, handle, void,
308 private_user_controller_t *this, fast_request_t *request, char *action,
309 char *p2, char *p3, char *p4, char *p5)
310 {
311 if (action)
312 {
313 if (streq(action, "add"))
314 {
315 return add(this, request);
316 }
317 if (streq(action, "login"))
318 {
319 return login(this, request);
320 }
321 else if (streq(action, "logout"))
322 {
323 return logout(this, request);
324 }
325 else if (streq(action, "edit"))
326 {
327 return edit(this, request);
328 }
329 else if (streq(action, "help"))
330 {
331 return request->render(request, "templates/user/help.cs");
332 }
333 }
334 request->redirect(request, "user/login");
335 }
336
337 METHOD(fast_controller_t, destroy, void,
338 private_user_controller_t *this)
339 {
340 free(this);
341 }
342
343 /*
344 * see header file
345 */
346 fast_controller_t *user_controller_create(user_t *user, database_t *db)
347 {
348 private_user_controller_t *this;
349
350 INIT(this,
351 .public = {
352 .controller = {
353 .get_name = _get_name,
354 .handle = _handle,
355 .destroy = _destroy,
356 },
357 },
358 .user = user,
359 .db = db,
360 .password_length = lib->settings->get_int(lib->settings,
361 "medsrv.password_length", 6),
362 );
363
364 return &this->public.controller;
365 }