GCC Code Coverage Report


Directory: ./
File: panels/system/users/cc-password-dialog.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 220 0.0%
Functions: 0 25 0.0%
Branches: 0 76 0.0%

Line Branch Exec Source
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright 2009-2010 Red Hat, Inc,
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Written by: Matthias Clasen <mclasen@redhat.com>
19 */
20
21 #include "config.h"
22
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27
28 #include <adwaita.h>
29 #include <glib.h>
30 #include <glib/gi18n.h>
31 #include <gtk/gtk.h>
32 #include <act/act.h>
33
34 #include "cc-password-dialog.h"
35 #include "pw-utils.h"
36 #include "run-passwd.h"
37 #include "user-utils.h"
38
39 #define PASSWORD_CHECK_TIMEOUT 600
40
41 struct _CcPasswordDialog
42 {
43 AdwDialog parent_instance;
44
45 GtkCheckButton *action_login_radio;
46 GtkCheckButton *action_now_radio;
47 GtkButton *generate_password_button;
48 GtkButton *ok_button;
49 AdwPasswordEntryRow *old_password_entry;
50 AdwPreferencesGroup *password_group;
51 AdwPreferencesGroup *password_on_next_login_group;
52 AdwPasswordEntryRow *password_entry;
53 GtkLabel *password_hint_label;
54 GtkLevelBar *strength_indicator;
55 AdwPasswordEntryRow *verify_entry;
56 GtkLabel *verify_label;
57
58 gint password_entry_timeout_id;
59
60 ActUser *user;
61 ActUserPasswordMode password_mode;
62
63 gboolean old_password_ok;
64 gint old_password_entry_timeout_id;
65
66 PasswdHandler *passwd_handler;
67 };
68
69 G_DEFINE_TYPE (CcPasswordDialog, cc_password_dialog, ADW_TYPE_DIALOG)
70
71 static gint
72 update_password_strength (CcPasswordDialog *self)
73 {
74 const gchar *password;
75 const gchar *old_password;
76 const gchar *username;
77 gint strength_level;
78 const gchar *hint;
79 const gchar *verify;
80
81 password = gtk_editable_get_text (GTK_EDITABLE (self->password_entry));
82 old_password = gtk_editable_get_text (GTK_EDITABLE (self->old_password_entry));
83 username = act_user_get_user_name (self->user);
84
85 pw_strength (password, old_password, username,
86 &hint, &strength_level);
87
88 gtk_level_bar_set_value (self->strength_indicator, strength_level);
89 gtk_label_set_label (self->password_hint_label, hint);
90
91 if (strength_level > 1) {
92 gtk_widget_remove_css_class (GTK_WIDGET (self->password_entry), "error");
93 } else if (strlen (password) == 0) {
94 //gtk_widget_set_visible (GTK_WIDGET (self->password_entry_status_icon), FALSE);
95 //gtk_widget_set_visible (GTK_WIDGET (self->generate_password_button), TRUE);
96 } else {
97 gtk_widget_add_css_class (GTK_WIDGET (self->password_entry), "error");
98 }
99
100 verify = gtk_editable_get_text (GTK_EDITABLE (self->verify_entry));
101 if (strlen (verify) == 0) {
102 gtk_widget_set_sensitive (GTK_WIDGET (self->verify_entry), strength_level > 1);
103 }
104
105 return strength_level;
106 }
107
108 static void
109 password_changed_cb (PasswdHandler *handler,
110 GError *error,
111 CcPasswordDialog *self)
112 {
113 AdwDialog *dialog;
114 const gchar *primary_text;
115 const gchar *secondary_text;
116
117 gtk_widget_set_sensitive (GTK_WIDGET (self), TRUE);
118
119 if (!error) {
120 adw_dialog_close (ADW_DIALOG (self));
121 return;
122 }
123
124 if (error->code == PASSWD_ERROR_REJECTED) {
125 primary_text = error->message;
126 secondary_text = _("Please choose another password.");
127
128 gtk_editable_set_text (GTK_EDITABLE (self->password_entry), "");
129 gtk_widget_grab_focus (GTK_WIDGET (self->password_entry));
130
131 gtk_editable_set_text (GTK_EDITABLE (self->verify_entry), "");
132 }
133 else if (error->code == PASSWD_ERROR_AUTH_FAILED) {
134 primary_text = error->message;
135 secondary_text = _("Please type your current password again.");
136
137 gtk_editable_set_text (GTK_EDITABLE (self->old_password_entry), "");
138 gtk_widget_grab_focus (GTK_WIDGET (self->old_password_entry));
139 }
140 else {
141 primary_text = _("Password could not be changed");
142 secondary_text = error->message;
143 }
144
145 dialog = adw_alert_dialog_new (primary_text, secondary_text);
146 adw_alert_dialog_add_responses (ADW_ALERT_DIALOG (dialog),
147 "ok", _("_OK"),
148 NULL);
149
150 adw_alert_dialog_set_default_response (ADW_ALERT_DIALOG (dialog),
151 "ok");
152
153 adw_dialog_present (dialog, GTK_WIDGET (self));
154 }
155
156 static void
157 ok_button_clicked_cb (CcPasswordDialog *self)
158 {
159 const gchar *password;
160
161 password = gtk_editable_get_text (GTK_EDITABLE (self->password_entry));
162
163 switch (self->password_mode) {
164 case ACT_USER_PASSWORD_MODE_REGULAR:
165 if (act_user_get_uid (self->user) == getuid ()) {
166 /* When setting a password for the current user,
167 * use passwd directly, to preserve the audit trail
168 * and to e.g. update the keyring password.
169 */
170 passwd_change_password (self->passwd_handler, password,
171 (PasswdCallback) password_changed_cb, self);
172 gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
173 return;
174 }
175
176 act_user_set_password_mode (self->user, ACT_USER_PASSWORD_MODE_REGULAR);
177 act_user_set_password (self->user, password, "");
178 break;
179
180 case ACT_USER_PASSWORD_MODE_SET_AT_LOGIN:
181 act_user_set_password_mode (self->user, self->password_mode);
182 act_user_set_automatic_login (self->user, FALSE);
183 break;
184
185 default:
186 g_assert_not_reached ();
187 }
188
189 adw_dialog_close (ADW_DIALOG (self));
190 }
191
192 static void
193 update_sensitivity (CcPasswordDialog *self)
194 {
195 const gchar *password, *verify;
196 gboolean can_change;
197 int strength;
198
199 password = gtk_editable_get_text (GTK_EDITABLE (self->password_entry));
200 verify = gtk_editable_get_text (GTK_EDITABLE (self->verify_entry));
201
202 if (self->password_mode == ACT_USER_PASSWORD_MODE_REGULAR) {
203 strength = update_password_strength (self);
204 can_change = strength > 1 && strcmp (password, verify) == 0 &&
205 (self->old_password_ok || !gtk_widget_get_visible (GTK_WIDGET (self->old_password_entry)));
206 }
207 else {
208 can_change = TRUE;
209 }
210
211 gtk_widget_set_sensitive (GTK_WIDGET (self->password_entry), self->old_password_ok);
212 gtk_widget_set_sensitive (GTK_WIDGET (self->verify_entry), self->old_password_ok);
213 gtk_widget_set_sensitive (GTK_WIDGET (self->ok_button), can_change);
214 }
215
216 static void
217 mode_change (CcPasswordDialog *self,
218 ActUserPasswordMode mode)
219 {
220 gboolean active;
221
222 active = (mode == ACT_USER_PASSWORD_MODE_REGULAR);
223 gtk_widget_set_sensitive (GTK_WIDGET (self->password_entry), self->old_password_ok);
224 gtk_widget_set_sensitive (GTK_WIDGET (self->verify_entry), self->old_password_ok);
225 gtk_widget_set_sensitive (GTK_WIDGET (self->old_password_entry), active);
226 gtk_check_button_set_active (GTK_CHECK_BUTTON (self->action_now_radio), active);
227 gtk_check_button_set_active (GTK_CHECK_BUTTON (self->action_login_radio), !active);
228
229 self->password_mode = mode;
230 update_sensitivity (self);
231 }
232
233 static void
234 action_now_radio_toggled_cb (CcPasswordDialog *self)
235 {
236 gint active;
237 ActUserPasswordMode mode;
238
239 active = gtk_check_button_get_active (GTK_CHECK_BUTTON (self->action_now_radio));
240 mode = active ? ACT_USER_PASSWORD_MODE_REGULAR : ACT_USER_PASSWORD_MODE_SET_AT_LOGIN;
241 mode_change (self, mode);
242 }
243
244 static void
245 update_password_match (CcPasswordDialog *self)
246 {
247 const gchar *password;
248 const gchar *verify;
249
250 password = gtk_editable_get_text (GTK_EDITABLE (self->password_entry));
251 verify = gtk_editable_get_text (GTK_EDITABLE (self->verify_entry));
252
253 if (strlen (verify) > 0) {
254 if (strcmp (password, verify) != 0) {
255 gtk_widget_set_visible (GTK_WIDGET (self->verify_label), TRUE);
256 gtk_widget_add_css_class (GTK_WIDGET (self->verify_entry), "error");
257 }
258 else {
259 gtk_widget_set_visible (GTK_WIDGET (self->verify_label), FALSE);
260 gtk_widget_remove_css_class (GTK_WIDGET (self->verify_entry), "error");
261
262 }
263 }
264 }
265
266 static gboolean
267 password_entry_timeout (CcPasswordDialog *self)
268 {
269 update_password_strength (self);
270 update_sensitivity (self);
271 update_password_match (self);
272
273 self->password_entry_timeout_id = 0;
274
275 return FALSE;
276 }
277
278 static void
279 recheck_password_match (CcPasswordDialog *self)
280 {
281 g_clear_handle_id (&self->password_entry_timeout_id, g_source_remove);
282
283 gtk_widget_set_sensitive (GTK_WIDGET (self->ok_button), FALSE);
284
285 self->password_entry_timeout_id = g_timeout_add (PASSWORD_CHECK_TIMEOUT,
286 (GSourceFunc) password_entry_timeout,
287 self);
288 }
289
290 static void
291 password_entry_changed (CcPasswordDialog *self)
292 {
293 gtk_widget_add_css_class (GTK_WIDGET (self->password_entry), "error");
294 gtk_widget_add_css_class (GTK_WIDGET (self->verify_entry), "error");
295 recheck_password_match (self);
296 }
297
298 static void
299 verify_entry_changed (CcPasswordDialog *self)
300 {
301 gtk_widget_add_css_class (GTK_WIDGET (self->verify_entry), "error");
302 recheck_password_match (self);
303 }
304
305 static gboolean
306 password_entry_focus_out_cb (CcPasswordDialog *self)
307 {
308 g_clear_handle_id (&self->password_entry_timeout_id, g_source_remove);
309
310 if (self->user != NULL)
311 password_entry_timeout (self);
312
313 return FALSE;
314 }
315
316
317 static gboolean
318 password_entry_key_press_cb (CcPasswordDialog *self,
319 guint keyval,
320 guint keycode,
321 GdkModifierType state,
322 GtkEventControllerKey *controller)
323 {
324 g_clear_handle_id (&self->password_entry_timeout_id, g_source_remove);
325
326 if (keyval == GDK_KEY_Tab)
327 password_entry_timeout (self);
328
329 return FALSE;
330 }
331
332 static void
333 auth_cb (PasswdHandler *handler,
334 GError *error,
335 CcPasswordDialog *self)
336 {
337 if (error) {
338 self->old_password_ok = FALSE;
339 }
340 else {
341 self->old_password_ok = TRUE;
342 gtk_widget_remove_css_class (GTK_WIDGET (self->old_password_entry), "error");
343 }
344
345 update_sensitivity (self);
346 }
347
348 static gboolean
349 old_password_entry_timeout (CcPasswordDialog *self)
350 {
351 const gchar *text;
352
353 update_sensitivity (self);
354
355 text = gtk_editable_get_text (GTK_EDITABLE (self->old_password_entry));
356 if (!self->old_password_ok) {
357 passwd_authenticate (self->passwd_handler, text, (PasswdCallback)auth_cb, self);
358 }
359
360 self->old_password_entry_timeout_id = 0;
361
362 return FALSE;
363 }
364
365 static gboolean
366 old_password_entry_focus_out_cb (CcPasswordDialog *self)
367 {
368 g_clear_handle_id (&self->old_password_entry_timeout_id, g_source_remove);
369
370 if (self->user != NULL)
371 old_password_entry_timeout (self);
372
373 return FALSE;
374 }
375
376 static void
377 old_password_entry_changed (CcPasswordDialog *self)
378 {
379 g_clear_handle_id (&self->old_password_entry_timeout_id, g_source_remove);
380
381 gtk_widget_add_css_class (GTK_WIDGET (self->old_password_entry), "error");
382 gtk_widget_set_sensitive (GTK_WIDGET (self->ok_button), FALSE);
383
384 self->old_password_ok = FALSE;
385 gtk_widget_set_sensitive (GTK_WIDGET (self->password_entry), self->old_password_ok);
386 gtk_widget_set_sensitive (GTK_WIDGET (self->verify_entry), self->old_password_ok);
387 self->old_password_entry_timeout_id = g_timeout_add (PASSWORD_CHECK_TIMEOUT,
388 (GSourceFunc) old_password_entry_timeout,
389 self);
390 }
391
392 static void
393 generate_password (CcPasswordDialog *self)
394 {
395 g_autofree gchar *pwd = NULL;
396
397 pwd = pw_generate ();
398 if (pwd == NULL)
399 return;
400
401 gtk_editable_set_text (GTK_EDITABLE (self->password_entry), pwd);
402 gtk_editable_set_text (GTK_EDITABLE (self->verify_entry), pwd);
403 gtk_widget_set_sensitive (GTK_WIDGET (self->verify_entry), TRUE);
404
405 gtk_widget_set_visible (GTK_WIDGET (self->generate_password_button), FALSE);
406 }
407
408 static void
409 cc_password_dialog_dispose (GObject *object)
410 {
411 CcPasswordDialog *self = CC_PASSWORD_DIALOG (object);
412
413 g_clear_object (&self->user);
414
415 if (self->passwd_handler) {
416 passwd_destroy (self->passwd_handler);
417 self->passwd_handler = NULL;
418 }
419
420 g_clear_handle_id (&self->old_password_entry_timeout_id, g_source_remove);
421 g_clear_handle_id (&self->password_entry_timeout_id, g_source_remove);
422
423 G_OBJECT_CLASS (cc_password_dialog_parent_class)->dispose (object);
424 }
425
426 static void
427 cc_password_dialog_class_init (CcPasswordDialogClass *klass)
428 {
429 GObjectClass *object_class = G_OBJECT_CLASS (klass);
430 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
431
432 object_class->dispose = cc_password_dialog_dispose;
433
434 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/system/users/cc-password-dialog.ui");
435
436 gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, action_login_radio);
437 gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, action_now_radio);
438 gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, generate_password_button);
439 gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, ok_button);
440 gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, old_password_entry);
441 gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, password_group);
442 gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, password_on_next_login_group);
443 gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, password_entry);
444 gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, password_hint_label);
445 gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, strength_indicator);
446 gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, verify_entry);
447 gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, verify_label);
448
449 gtk_widget_class_bind_template_callback (widget_class, action_now_radio_toggled_cb);
450 gtk_widget_class_bind_template_callback (widget_class, generate_password);
451 gtk_widget_class_bind_template_callback (widget_class, old_password_entry_changed);
452 gtk_widget_class_bind_template_callback (widget_class, old_password_entry_focus_out_cb);
453 gtk_widget_class_bind_template_callback (widget_class, ok_button_clicked_cb);
454 gtk_widget_class_bind_template_callback (widget_class, password_entry_changed);
455 gtk_widget_class_bind_template_callback (widget_class, password_entry_focus_out_cb);
456 gtk_widget_class_bind_template_callback (widget_class, password_entry_key_press_cb);
457 gtk_widget_class_bind_template_callback (widget_class, verify_entry_changed);
458 }
459
460 static void
461 cc_password_dialog_init (CcPasswordDialog *self)
462 {
463 gtk_widget_init_template (GTK_WIDGET (self));
464 }
465
466 CcPasswordDialog *
467 cc_password_dialog_new (ActUser *user)
468 {
469 CcPasswordDialog *self;
470
471 g_return_val_if_fail (ACT_IS_USER (user), NULL);
472
473 self = g_object_new (CC_TYPE_PASSWORD_DIALOG,
474 NULL);
475
476 self->user = g_object_ref (user);
477
478 if (act_user_get_uid (self->user) == getuid ()) {
479 gboolean visible;
480
481 mode_change (self, ACT_USER_PASSWORD_MODE_REGULAR);
482 gtk_widget_set_visible (GTK_WIDGET (self->password_on_next_login_group), FALSE);
483
484 visible = (act_user_get_password_mode (user) != ACT_USER_PASSWORD_MODE_NONE);
485 gtk_widget_set_visible (GTK_WIDGET (self->old_password_entry), visible);
486 self->old_password_ok = !visible;
487
488 self->passwd_handler = passwd_init ();
489 }
490 else {
491 mode_change (self, ACT_USER_PASSWORD_MODE_SET_AT_LOGIN);
492 gtk_widget_set_visible (GTK_WIDGET (self->password_on_next_login_group), TRUE);
493
494 gtk_widget_set_visible (GTK_WIDGET (self->old_password_entry), FALSE);
495 self->old_password_ok = TRUE;
496 }
497
498 if (self->old_password_ok == FALSE)
499 gtk_widget_grab_focus (GTK_WIDGET (self->old_password_entry));
500 else
501 gtk_widget_grab_focus (GTK_WIDGET (self->password_entry));
502
503 adw_dialog_set_default_widget (ADW_DIALOG (self), GTK_WIDGET (self->ok_button));
504
505 return self;
506 }
507