merging changes from portability branch back to trunk
[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 * $Id$
17 */
18
19 #define _GNU_SOURCE
20 #include <string.h>
21
22 #include "user_controller.h"
23
24 #include <library.h>
25
26 typedef struct private_user_controller_t private_user_controller_t;
27
28 /**
29 * private data of the user_controller
30 */
31 struct private_user_controller_t {
32
33 /**
34 * public functions
35 */
36 user_controller_t public;
37
38 /**
39 * database connection
40 */
41 database_t *db;
42
43 /**
44 * user session
45 */
46 user_t *user;
47
48 /**
49 * minimum required password lenght
50 */
51 u_int password_length;
52 };
53
54 /**
55 * hash the password for database storage
56 */
57 static chunk_t hash_password(char *login, char *password)
58 {
59 hasher_t *hasher;
60 chunk_t hash, data;
61
62 hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
63 if (!hasher)
64 {
65 return chunk_empty;
66 }
67 data = chunk_cata("cc", chunk_create(login, strlen(login)),
68 chunk_create(password, strlen(password)));
69 hasher->allocate_hash(hasher, data, &hash);
70 hasher->destroy(hasher);
71 return hash;
72 }
73
74 /**
75 * Login a user.
76 */
77 static void login(private_user_controller_t *this, request_t *request)
78 {
79 if (request->get_query_data(request, "submit"))
80 {
81 char *login, *password;
82
83 login = request->get_query_data(request, "login");
84 password = request->get_query_data(request, "password");
85
86 if (login && password)
87 {
88 enumerator_t *query;
89 u_int id = 0;
90 chunk_t hash;
91
92 hash = hash_password(login, password);
93 query = this->db->query(this->db,
94 "SELECT id FROM user WHERE login = ? AND password = ?",
95 DB_TEXT, login, DB_BLOB, hash, DB_UINT);
96 if (query)
97 {
98 query->enumerate(query, &id);
99 query->destroy(query);
100 }
101 free(hash.ptr);
102 if (id)
103 {
104 this->user->set_user(this->user, id);
105 return request->redirect(request, "peer/list");
106 }
107 }
108 request->setf(request, "error=Invalid username or password.");
109 }
110 request->render(request, "templates/user/login.cs");
111 }
112
113 /**
114 * Logout a user.
115 */
116 static void logout(private_user_controller_t *this, request_t *request)
117 {
118 request->redirect(request, "user/login");
119 request->close_session(request);
120 }
121
122 /**
123 * verify a user entered username for validity
124 */
125 static bool verify_login(private_user_controller_t *this, request_t *request,
126 char *login)
127 {
128 if (!login || *login == '\0')
129 {
130 request->setf(request, "error=Username is missing.");
131 return FALSE;
132 }
133 while (*login != '\0')
134 {
135 switch (*login)
136 {
137 case 'a' ... 'z':
138 case 'A' ... 'Z':
139 case '0' ... '9':
140 case '-':
141 case '_':
142 case '@':
143 case '.':
144 login++;
145 continue;
146 default:
147 request->setf(request, "error=Username invalid, "
148 "valid characters: A-Z a-z 0-9 - _ @ .");
149 }
150 }
151 return TRUE;
152 }
153
154 /**
155 * verify a user entered password for validity
156 */
157 static bool verify_password(private_user_controller_t *this, request_t *request,
158 char *password, char *confirm)
159 {
160 if (!password || *password == '\0')
161 {
162 request->setf(request, "error=Password is missing.");
163 return FALSE;
164 }
165 if (strlen(password) < this->password_length)
166 {
167 request->setf(request, "error=Password requires at least %d characters.",
168 this->password_length);
169 return FALSE;
170 }
171 if (!confirm || !streq(password, confirm))
172 {
173 request->setf(request, "error=Password not confirmed.");
174 return FALSE;
175 }
176 return TRUE;
177 }
178
179 /**
180 * Register a user.
181 */
182 static void add(private_user_controller_t *this, request_t *request)
183 {
184 char *login = "";
185
186 while (request->get_query_data(request, "register"))
187 {
188 char *password, *confirm;
189 chunk_t hash;
190 u_int id;
191
192 login = request->get_query_data(request, "new_login");
193 password = request->get_query_data(request, "new_password");
194 confirm = request->get_query_data(request, "confirm_password");
195
196 if (!verify_login(this, request, login) ||
197 !verify_password(this, request, password, confirm))
198 {
199 break;
200 }
201
202 hash = hash_password(login, password);
203 if (!hash.ptr || this->db->execute(this->db, &id,
204 "INSERT INTO user (login, password) VALUES (?, ?)",
205 DB_TEXT, login, DB_BLOB, hash) < 0)
206 {
207 request->setf(request, "error=Username already exists.");
208 free(hash.ptr);
209 break;
210 }
211 free(hash.ptr);
212 this->user->set_user(this->user, id);
213 return request->redirect(request, "peer/list");
214 }
215 request->set(request, "new_login", login);
216 request->setf(request, "password_length=%d", this->password_length);
217 request->render(request, "templates/user/add.cs");
218 }
219
220 /**
221 * Edit the logged in user
222 */
223 static void edit(private_user_controller_t *this, request_t *request)
224 {
225 enumerator_t *query;
226 char *old_login;
227
228 /* lookup old login */
229 query = this->db->query(this->db, "SELECT login FROM user WHERE id = ?",
230 DB_INT, this->user->get_user(this->user),
231 DB_TEXT);
232 if (!query || !query->enumerate(query, &old_login))
233 {
234 DESTROY_IF(query);
235 request->close_session(request);
236 return request->redirect(request, "user/login");
237 }
238 old_login = strdupa(old_login);
239 query->destroy(query);
240
241 /* back pressed */
242 if (request->get_query_data(request, "back"))
243 {
244 return request->redirect(request, "peer/list");
245 }
246 /* delete pressed */
247 if (request->get_query_data(request, "delete"))
248 {
249 this->db->execute(this->db, NULL, "DELETE FROM user WHERE id = ?",
250 DB_UINT, this->user->get_user(this->user));
251 this->db->execute(this->db, NULL,
252 "DELETE FROM peer WHERE user = ?",
253 DB_UINT, this->user->get_user(this->user));
254 return logout(this, request);
255 }
256 /* save pressed */
257 while (request->get_query_data(request, "save"))
258 {
259 char *new_login, *old_pass, *new_pass, *confirm;
260 chunk_t old_hash, new_hash;
261
262 new_login = request->get_query_data(request, "old_login");
263 old_pass = request->get_query_data(request, "old_password");
264 new_pass = request->get_query_data(request, "new_password");
265 confirm = request->get_query_data(request, "confirm_password");
266
267 if (!verify_login(this, request, new_login) ||
268 !verify_password(this, request, new_pass, confirm))
269 {
270 old_login = new_login;
271 break;
272 }
273 old_hash = hash_password(old_login, old_pass);
274 new_hash = hash_password(new_login, new_pass);
275
276 if (this->db->execute(this->db, NULL,
277 "UPDATE user SET login = ?, password = ? "
278 "WHERE id = ? AND password = ?",
279 DB_TEXT, new_login, DB_BLOB, new_hash,
280 DB_UINT, this->user->get_user(this->user), DB_BLOB, old_hash) <= 0)
281 {
282 free(new_hash.ptr);
283 free(old_hash.ptr);
284 old_login = new_login;
285 request->setf(request, "error=Password verification failed.");
286 break;
287 }
288 free(new_hash.ptr);
289 free(old_hash.ptr);
290 return request->redirect(request, "peer/list");
291 }
292 /* on error/template rendering */
293 request->set(request, "old_login", old_login);
294 request->setf(request, "password_length=%d", this->password_length);
295 request->render(request, "templates/user/edit.cs");
296 }
297
298 /**
299 * Implementation of controller_t.get_name
300 */
301 static char* get_name(private_user_controller_t *this)
302 {
303 return "user";
304 }
305
306 /**
307 * Implementation of controller_t.handle
308 */
309 static void handle(private_user_controller_t *this, request_t *request, char *action)
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 /**
338 * Implementation of controller_t.destroy
339 */
340 static void destroy(private_user_controller_t *this)
341 {
342 free(this);
343 }
344
345 /*
346 * see header file
347 */
348 controller_t *user_controller_create(user_t *user, database_t *db)
349 {
350 private_user_controller_t *this= malloc_thing(private_user_controller_t);
351
352 this->public.controller.get_name = (char*(*)(controller_t*))get_name;
353 this->public.controller.handle = (void(*)(controller_t*, request_t*, char*, char*, char*, char*, char*))handle;
354 this->public.controller.destroy = (void(*)(controller_t*))destroy;
355
356 this->user = user;
357 this->db = db;
358 this->password_length = lib->settings->get_int(lib->settings,
359 "medsrv.password_length", 6);
360
361 return &this->public.controller;
362 }
363