• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

yast / yast-users / 5453098972

pending completion
5453098972

Pull #383

github

web-flow
Merge 54d62cefa into f91b954c3
Pull Request #383: User creation page got messed when going back after importing user(s) from existing installation

16 of 16 new or added lines in 1 file covered. (100.0%)

3319 of 5513 relevant lines covered (60.2%)

35.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

10.88
/src/include/users/dialogs.rb
1
# encoding: utf-8
2

3
# ------------------------------------------------------------------------------
4
# Copyright (c) 2006-2012 Novell, Inc. All Rights Reserved.
5
#
6
#
7
# This program is free software; you can redistribute it and/or modify it under
8
# the terms of version 2 of the GNU General Public License as published by the
9
# Free Software Foundation.
10
#
11
# This program is distributed in the hope that it will be useful, but WITHOUT
12
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License along with
16
# this program; if not, contact Novell, Inc.
17
#
18
# To contact Novell about this file by physical or electronic mail, you may find
19
# current contact information at www.novell.com.
20
# ------------------------------------------------------------------------------
21

22
# File:        include/users/dialogs.rb
23
# Package:        Configuration of users and groups
24
# Summary:        Wizards definitions
25
# Authors:        Johannes Buchhold <jbuch@suse.de>,
26
#          Jiri Suchomel <jsuchome@suse.cz>
27
#
28
# $Id$
29

30
require "pathname"
1✔
31
require "shellwords"
1✔
32

33
require "users/ssh_public_key"
1✔
34
require "yast2/popup"
1✔
35
require "y2users/username"
1✔
36

37
module Yast
1✔
38
  module UsersDialogsInclude
1✔
39
    def initialize_users_dialogs(include_target)
1✔
40
      textdomain "users"
42✔
41

42
      Yast.import "Autologin"
42✔
43
      Yast.import "GetInstArgs"
42✔
44
      Yast.import "FileUtils"
42✔
45
      Yast.import "Label"
42✔
46
      Yast.import "Ldap"
42✔
47
      Yast.import "LdapPopup"
42✔
48
      Yast.import "Message"
42✔
49
      Yast.import "Package"
42✔
50
      Yast.import "Popup"
42✔
51
      Yast.import "ProductFeatures"
42✔
52
      Yast.import "Report"
42✔
53
      Yast.import "Stage"
42✔
54
      Yast.import "String"
42✔
55
      Yast.import "Users"
42✔
56
      Yast.import "UsersCache"
42✔
57
      Yast.import "UsersLDAP"
42✔
58
      Yast.import "UsersPlugins"
42✔
59
      Yast.import "UsersRoutines"
42✔
60
      Yast.import "UsersSimple"
42✔
61
      Yast.import "Wizard"
42✔
62
      Yast.import "Mode"
42✔
63

64
      Yast.include include_target, "users/helps.rb"
42✔
65
      Yast.include include_target, "users/routines.rb"
42✔
66

67
      @default_pw = "******"
42✔
68
    end
69

70
    # Uppercase letters were used in username! (see bug #26409)
71
    # In these popups, ask user what to do.
72
    def AskForUppercasePopup(username)
1✔
73
      ret = :ok
×
74

75
      if username != Builtins.tolower(username) && !Users.NotAskUppercase &&
×
76
          Package.InstalledAny(["sendmail", "postfix"])
77
        # The login name contains uppercase 1/3
78
        text = _(
×
79
          "<p>\nYou have used uppercase letters in the user login entry.</p>"
80
        ) +
81
          # The login name contains uppercase 2/3
82
          _(
83
            "<p>This could cause problems with delivering mail\n" +
84
              "to this user, because mail systems generally do not\n" +
85
              "support case-sensitive names.<br>\n" +
86
              "You could solve this problem by editing the alias table.</p>\n"
87
          ) +
88
          # The login name contains uppercase 3/3
89
          _("<p>Really use the entered value?</p>")
90

91
        UI.OpenDialog(
×
92
          Opt(:decorated),
93
          HBox(
94
            VSpacing(14),
95
            VBox(
96
              HSpacing(50),
97
              RichText(Id(:rt), text),
98
              CheckBox(Id(:ch), Opt(:notify), Message.DoNotShowMessageAgain),
99
              HBox(
100
                PushButton(Id(:ok), Opt(:key_F10), Label.YesButton),
101
                PushButton(Id(:no), Opt(:key_F9), Label.NoButton)
102
              )
103
            )
104
          )
105
        )
106
        begin
×
107
          ret = Convert.to_symbol(UI.UserInput)
×
108
        end while !Builtins.contains([:cancel, :ok, :no], ret)
109

110
        if ret != :cancel
×
111
          Users.SetAskUppercase(
×
112
            Convert.to_boolean(UI.QueryWidget(Id(:ch), :Value))
113
          )
114
        end
115
        UI.CloseDialog
×
116
      end
117
      ret
×
118
    end
119

120
    # Ask user for current password, see bugs 242531, 244718
121
    # @return [String] or nil when dialog was canceled
122
    def AskForOldPassword
1✔
123
      UI.OpenDialog(
×
124
        Opt(:decorated),
125
        HBox(
126
          HSpacing(0.5),
127
          VBox(
128
            VSpacing(0.5),
129
            # password entry label
130
            Password(
131
              Id(:pw1),
132
              Opt(:hstretch),
133
              _(
134
                "To access the data required to modify\n" +
135
                  "the encryption settings for this user,\n" +
136
                  "enter the user's current password."
137
              )
138
            ),
139
            Password(Id(:pw2), Opt(:hstretch), Label.ConfirmPassword, ""),
140
            HBox(
141
              PushButton(Id(:ok), Opt(:key_F10), Label.OKButton),
142
              PushButton(Id(:cancel), Opt(:key_F9), Label.CancelButton)
143
            ),
144
            VSpacing(0.5)
145
          ),
146
          HSpacing(0.5)
147
        )
148
      )
149
      ret = :cancel
×
150
      begin
×
151
        ret = UI.UserInput
×
152
        if ret == :ok
×
153
          if UI.QueryWidget(Id(:pw1), :Value) !=
×
154
              UI.QueryWidget(Id(:pw2), :Value)
155
            Report.Error(_("The passwords do not match.\nTry again."))
×
156
            ret = :notnext
×
157
            next
×
158
          end
159
        end
160
      end until ret == :ok || ret == :cancel
161

162
      pw = Convert.to_string(UI.QueryWidget(Id(:pw1), :Value))
×
163
      UI.CloseDialog
×
164
      ret == :ok ? pw : nil
×
165
    end
166

167

168
    # helper function: show a popup if existing home directory should be used
169
    # and its ownership should be changed
170
    # @param dir [String]
171
    # @param chown_default [Boolean]
172
    def ask_chown_home(dir, chown_default)
1✔
173
      UI.OpenDialog(
2✔
174
        Opt(:decorated),
175
        HBox(
176
          HSpacing(1),
177
          VBox(
178
            VSpacing(0.2),
179
            Label(
180
              # popup label, %1 is path to directory
181
              Builtins.sformat(_("The home directory (%1) already exists.\nUse it anyway?"), dir)
182
            ),
183
            Left(
184
              # checkbox label
185
              CheckBox(Id(:chown_home), _("&Change directory owner"), chown_default)
186
            ),
187
            ButtonBox(
188
              PushButton(Id(:yes), Opt(:default), Label.YesButton),
189
              PushButton(Id(:no), Label.NoButton)
190
            ),
191
            VSpacing(0.2)
192
          ),
193
          HSpacing(1)
194
        )
195
      )
196
      answer = UI.UserInput == :yes
2✔
197
      retmap = { "retval" => answer }
2✔
198
      if answer
2✔
199
        retmap["chown_home"] = UI.QueryWidget(Id(:chown_home), :Value)
1✔
200
      end
201
      UI.CloseDialog
2✔
202
      retmap
2✔
203
    end
204

205
    # @param count [Integer] number of days after 1970-01-01
206
    # @param date_format [String] strftime format like "%x" (localized date)
207
    # @return [String]
208
    def format_days_after_epoch(count, date_format)
1✔
209
      `date --date='1970-01-01 00:00:01 #{count} days' +#{date_format}`.chomp
1✔
210
    end
211

212
    # generate contents for Password Settings Dialog
213
    # @param user [Hash]
214
    # @param exp_date [String] may be MODIFIED on return corresponding to the UI
215
    # @return [Term] ui_term
216
    def get_password_term(user, exp_date)
1✔
217
      last_change = GetString(Ops.get(user, "shadowLastChange"), "0")
1✔
218
      last_change_label = ""
1✔
219
      expires = GetString(Ops.get(user, "shadowExpire"), "0")
1✔
220
      expires = "0" if expires == ""
1✔
221

222
      inact = GetInt(Ops.get(user, "shadowInactive"), -1)
1✔
223
      max = GetInt(Ops.get(user, "shadowMax"), -1)
1✔
224
      min = GetInt(Ops.get(user, "shadowMin"), -1)
1✔
225
      warn = GetInt(Ops.get(user, "shadowWarning"), -1)
1✔
226

227
      if last_change != "0"
1✔
228
        last_change_label = format_days_after_epoch(last_change, "%x")
×
229
      else
230
        # label (date of last password change)
231
        last_change_label = _("Never")
1✔
232
      end
233
      unless ["0", "-1", ""].include?(expires)
1✔
234
        exp_date.replace(format_days_after_epoch(expires, "%Y-%m-%d"))
1✔
235
      end
236
      HBox(
1✔
237
        HSpacing(3),
238
        VBox(
239
          VStretch(),
240
          Left(Label("")),
241
          HSquash(
242
            VBox(
243
              Left(
244
                Label(
245
                  # label
246
                  Builtins.sformat(_("Last Password Change: %1"), last_change_label)
247
                )
248
              ),
249
              VSpacing(0.2),
250
              Left(
251
                # check box label
252
                CheckBox(Id(:force_pw), _("Force Password Change"), last_change == "0")
253
              ),
254
              VSpacing(1),
255
              IntField(
256
                Id("shadowWarning"),
257
                # intfield label
258
                _("Days &before Password Expiration to Issue Warning"),
259
                -1,
260
                99999,
261
                warn
262
              ),
263
              VSpacing(0.5),
264
              IntField(
265
                Id("shadowInactive"),
266
                # intfield label
267
                _("Days after Password Expires with Usable &Login"),
268
                -1,
269
                99999,
270
                inact
271
              ),
272
              VSpacing(0.5),
273
              IntField(
274
                Id("shadowMax"),
275
                # intfield label
276
                _("Ma&ximum Number of Days for the Same Password"),
277
                -1,
278
                99999,
279
                max
280
              ),
281
              VSpacing(0.5),
282
              IntField(
283
                Id("shadowMin"),
284
                # intfield label
285
                _("&Minimum Number of Days for the Same Password"),
286
                -1,
287
                99999,
288
                min
289
              ),
290
              VSpacing(0.5),
291
              InputField(
292
                Id("shadowExpire"),
293
                Opt(:hstretch),
294
                # textentry label
295
                _("Ex&piration Date"),
296
                exp_date
297
              )
298
            )
299
          ),
300
          VStretch()
301
        ),
302
        HSpacing(3)
303
      )
304
    end
305

306
    # Dialog for adding or editing a user.
307
    # @param [String] what "add_user" or "edit_user"
308
    # @return [Symbol] for wizard sequencer
309
    def EditUserDialog(what)
1✔
310
      # user has returned to the "add user dialog" during installation workflow:
311
      if Users.StartDialog("user_add") && installation && Users.UseNextTime
×
312
        Users.RestoreCurrentUser
×
313
        Users.SetUseNextTime(false)
×
314
      end
315

316
      display_info = UI.GetDisplayInfo
×
317
      text_mode = Ops.get_boolean(display_info, "TextMode", false)
×
318

319
      user = Users.GetCurrentUser
×
320
      error_msg = ""
×
321

322
      if user == {}
×
323
        error_msg = Users.AddUser({})
×
324
        if error_msg != ""
×
325
          Popup.Error(error_msg)
×
326
          return :back
×
327
        end
328
        user = Users.GetCurrentUser
×
329
      end
330

331
      action = Ops.get_string(user, "modified", "")
×
332
      action = what == "add_user" ? "added" : "edited" if action == ""
×
333

334
      user_type = Ops.get_string(user, "type", "local")
×
335
      username = Ops.get_string(user, "uid", "")
×
336
      cn = ""
×
337
      # in LDAP, cn is list of strings
338
      if Ops.is_list?(Ops.get(user, "cn"))
×
339
        cn = Ops.get_string(user, ["cn", 0], "")
×
340
      else
341
        cn = Ops.get_string(user, "cn", "")
×
342
      end
343
      default_home = Users.GetDefaultHome(user_type)
×
344
      home = Ops.get_string(user, "homeDirectory", default_home)
×
345
      org_home = Ops.get_string(user, "org_homeDirectory", home)
×
346
      default_mode = Builtins.sformat(
×
347
        "%1",
348
        Ops.subtract(777, Builtins.tointeger(String.CutZeros(Users.GetUmask)))
349
      )
350
      mode = Ops.get_string(user, "home_mode", default_mode)
×
351
      password = Ops.get_string(user, "userPassword")
×
352
      org_username = Ops.get_string(user, "org_uid", username)
×
353
      uid = GetInt(Ops.get(user, "uidNumber"), nil)
×
354
      gid = GetInt(Ops.get(user, "gidNumber"), Users.GetDefaultGID(user_type))
×
355
      enabled = Ops.get_boolean(user, "enabled", true)
×
356
      enabled = false if Ops.get_boolean(user, "disabled", false)
×
357

358
      shell = Ops.get_string(user, "loginShell", "")
×
359
      defaultgroup = Ops.get_string(user, "groupname", "")
×
360
      # additional parts of GECOS (shown by `finger <username>`) (passwd only)
361
      addit_data = Ops.get_string(user, "addit_data", "")
×
362

363
      # this user gets root's mail
364
      root_mail = Builtins.haskey(Users.GetRootAliases, username)
×
365
      root_mail_checked = root_mail
×
366

367
      # if user's password should be set to root
368
      root_pw = false
×
369

370
      # only for LDAP users:
371
      sn = ""
×
372
      if Builtins.haskey(user, "sn")
×
373
        if Ops.is_list?(Ops.get(user, "sn"))
×
374
          sn = Ops.get_string(user, ["sn", 0]) { SplitFullName(:sn, cn) }
×
375
        else
376
          sn = Ops.get_string(user, "sn") { SplitFullName(:sn, cn) }
×
377
        end
378
      end
379
      givenname = ""
×
380
      if Builtins.haskey(user, "givenName")
×
381
        if Ops.is_list?(Ops.get(user, "givenName"))
×
382
          givenname = Ops.get_string(user, ["givenName", 0]) do
×
383
            SplitFullName(:givenname, cn)
×
384
          end
385
        elsif Ops.is_string?(Ops.get(user, "givenName"))
×
386
          givenname = Ops.get_string(user, "givenName") do
×
387
            SplitFullName(:givenname, cn)
×
388
          end
389
        end
390
      end
391

392
      create_home = Ops.get_boolean(user, "create_home", true)
×
393
      chown_home = Ops.get_boolean(user, "chown_home", true)
×
394
      no_skel = Ops.get_boolean(user, "no_skeleton", false)
×
395
      if user["btrfs_subvolume"] && user["btrfs_subvolume"].is_a?(::String)
×
396
        # Users.GetCurrentUser Perl module can also return "1" for a boolean
397
        user["btrfs_subvolume"] = user["btrfs_subvolume"] == "1"
×
398
      end
399

400
      btrfs_subvolume = Ops.get_boolean(user, "btrfs_subvolume", false)
×
401
      do_not_edit = user_type == "nis"
×
402

403
      complex_layout = installation && Users.StartDialog("user_add")
×
404
      groups = Ops.get_map(user, "grouplist", {})
×
405

406
      available_shells = Users.AllShells
×
407
      new_type = user_type
×
408

409
      all_groupnames = UsersCache.GetAllGroupnames
×
410

411
      # backup NIS groups of user (they are not shown in details dialog)
412
      nis_groups = {}
×
413
      Builtins.foreach(groups) do |group, val|
×
414
        if Ops.get(all_groupnames, ["nis", group], 0) == 1
×
415
          Ops.set(nis_groups, group, 1)
×
416
        end
417
      end
418
      # of local group list of remote user was modified
419
      grouplist_modified = false
×
420

421
      # date of password expiration
422
      exp_date = ""
×
423

424
      plugin_client = ""
×
425
      plugin = ""
×
426
      client2plugin = {}
×
427
      # names of plugin GUI clients
428
      clients = []
×
429

430
      # initialize local variables with current state of user
431
      reinit_userdata = lambda do
×
432
        user_type = Ops.get_string(user, "type", user_type)
×
433
        username = Ops.get_string(user, "uid", username)
×
434
        if Ops.is_list?(Ops.get(user, "cn"))
×
435
          cn = Ops.get_string(user, ["cn", 0], cn)
×
436
        else
437
          cn = Ops.get_string(user, "cn", cn)
×
438
        end
439
        home = Ops.get_string(user, "homeDirectory", home)
×
440
        org_home = Ops.get_string(user, "org_homeDirectory", org_home)
×
441
        mode = Ops.get_string(user, "home_mode", default_mode)
×
442
        password = Ops.get_string(user, "userPassword", password)
×
443
        org_username = Ops.get_string(user, "org_uid", org_username)
×
444
        uid = GetInt(Ops.get(user, "uidNumber"), uid)
×
445
        gid = GetInt(Ops.get(user, "gidNumber"), gid)
×
446
        enabled = Ops.get_boolean(user, "enabled", true)
×
447
        enabled = false if Ops.get_boolean(user, "disabled", false)
×
448

449
        shell = Ops.get_string(user, "loginShell", shell)
×
450
        defaultgroup = Ops.get_string(user, "groupname", defaultgroup)
×
451
        addit_data = Ops.get_string(user, "addit_data", addit_data)
×
452

453
        if Builtins.haskey(user, "sn")
×
454
          if Ops.is_list?(Ops.get(user, "sn"))
×
455
            sn = Ops.get_string(user, ["sn", 0]) { SplitFullName(:sn, cn) }
×
456
          else
457
            sn = Ops.get_string(user, "sn") { SplitFullName(:sn, cn) }
×
458
          end
459
        end
460
        if Builtins.haskey(user, "givenName")
×
461
          if Ops.is_list?(Ops.get(user, "givenName"))
×
462
            givenname = Ops.get_string(user, ["givenName", 0]) do
×
463
              SplitFullName(:givenname, cn)
×
464
            end
465
          elsif Ops.is_string?(Ops.get(user, "givenName"))
×
466
            givenname = Ops.get_string(user, "givenName") do
×
467
              SplitFullName(:givenname, cn)
×
468
            end
469
          end
470
        end
471

472
        chown_home = Ops.get_boolean(user, "chown_home", chown_home)
×
473
        no_skel = Ops.get_boolean(user, "no_skeleton", no_skel)
×
474
        btrfs_subvolume = Ops.get_boolean(user, "btrfs_subvolume", btrfs_subvolume)
×
475
        groups = Ops.get_map(user, "grouplist", {})
×
476
        do_not_edit = user_type == "nis"
×
477

478
        nil
×
479
      end
480

481
      # generate contents for User Data Dialog
482
      get_edit_term = lambda do
×
483
        # text entry
484
        fullnamelabel = _("User's &Full Name")
×
485
        name_entries = what == "add_user" ?
×
486
          InputField(Id(:cn), Opt(:notify), fullnamelabel, cn) :
487
          InputField(Id(:cn), fullnamelabel, cn)
488

489
        if user_type == "ldap"
×
490
          name_entries = HBox(
×
491
            # text entry
492
            InputField(Id(:givenname), _("&First Name"), givenname),
493
            HSpacing(0.8),
494
            # text entry
495
            InputField(Id(:sn), _("&Last Name"), sn)
496
          )
497
        end
498

499
        fields = VBox(
×
500
          # label text
501
          do_not_edit ?
×
502
            Label(
503
              _(
504
                "For remote users, only additional group memberships can be changed."
505
              )
506
            ) :
507
            VSpacing(0),
508
          do_not_edit ? VSpacing(1) : VSpacing(0),
×
509
          name_entries,
510
          InputField(
511
            Id(:username),
512
            what == "add_user" ? Opt(:notify, :hstretch) : Opt(:hstretch),
×
513
            # input field for login name
514
            _("&Username"),
515
            username
516
          ),
517
          VSpacing(),
518
          Password(Id(:pw1), Opt(:hstretch), Label.Password, ""),
519
          Password(Id(:pw2), Opt(:hstretch), Label.ConfirmPassword, "")
520
        )
521

522
        optionbox = VBox(
×
523
          # checkbox label
524
          Left(
525
            CheckBox(
526
              Id(:root_mail),
527
              _("Receive S&ystem Mail"),
528
              root_mail_checked
529
            )
530
          )
531
        )
532
        if complex_layout
×
533
          optionbox = Builtins.add(
×
534
            optionbox,
535
            # checkbox label
536
            Left(
537
              CheckBox(Id(:autologin), _("A&utomatic Login"), Autologin.used)
538
            )
539
          )
540
          root_pw_feature = ProductFeatures.GetFeature(
×
541
            "globals",
542
            "root_password_as_first_user"
543
          )
544
          if root_pw_feature != ""
×
545
            optionbox = Builtins.add(
×
546
              optionbox,
547
              Left(
548
                CheckBox(
549
                  Id(:root_pw),
550
                  # checkbox label
551
                  _("U&se this password for system administrator"),
552
                  root_pw_feature == true
553
                )
554
              )
555
            )
556
          end
557
        elsif !do_not_edit && !installation
×
558
          optionbox = Builtins.add(
×
559
            optionbox,
560
            # check box label
561
            Left(CheckBox(Id(:ena), _("D&isable User Login"), !enabled))
562
          )
563
        end
564
        contents = VBox(
×
565
          VSpacing(),
566
          VBox(
567
            VSpacing(0.5),
568
            HBox(
569
              HSpacing(2),
570
              VBox(
571
                HSquash(fields),
572
                VSpacing(0.5),
573
                HBox(HStretch(), HCenter(HVSquash(optionbox)), HStretch())
574
              ),
575
              HSpacing(2)
576
            ),
577
            VSpacing(0.5)
578
          )
579
        )
580

581
        if complex_layout
×
582
          contents = Builtins.add(
×
583
            contents,
584
            VBox(
585
              HCenter(
586
                PushButton(
587
                  Id(:additional),
588
                  Opt(:key_F3),
589
                  # push button
590
                  _("User &Management")
591
                )
592
              ),
593
              VSpacing(0.5)
594
            )
595
          )
596
        end
597
        HVCenter(contents)
×
598
      end
599

600
      # generate contents for User Details Dialog
601
      get_details_term = lambda do
×
602
        available_groups = []
×
603
        additional_groups = []
×
604
        additional_ldap_groups = []
×
605
        defaultgroup_shown = false
×
606

607
        # fill the list available_groups and set the user default group true
608
        Builtins.foreach(all_groupnames) do |grouptype, groupmap|
×
609
          if grouptype == "local" || grouptype == "system" ||
×
610
              grouptype == "ldap" && user_type == "ldap"
611
            Builtins.foreach(groupmap) do |group, val|
×
612
              if user_type == "ldap"
×
613
                if grouptype == "ldap"
×
614
                  if group == defaultgroup
×
615
                    available_groups = Builtins.add(
×
616
                      available_groups,
617
                      Item(Id(group), group, true)
618
                    )
619
                    defaultgroup_shown = true
×
620
                  else
621
                    available_groups = Builtins.add(
×
622
                      available_groups,
623
                      Item(Id(group), group)
624
                    )
625
                  end
626
                  if Builtins.haskey(groups, group)
×
627
                    additional_ldap_groups = Builtins.add(
×
628
                      additional_ldap_groups,
629
                      Item(Id(group), group, true)
630
                    )
631
                  else
632
                    additional_ldap_groups = Builtins.add(
×
633
                      additional_ldap_groups,
634
                      Item(Id(group), group, false)
635
                    )
636
                  end
637
                else
638
                  # if there is a group with same name, use only that
639
                  # with type "ldap"
640
                  next if Ops.get(all_groupnames, ["ldap", group], 0) == 1
×
641
                  if Builtins.haskey(groups, group)
×
642
                    additional_groups = Builtins.add(
×
643
                      additional_groups,
644
                      Item(Id(group), group, true)
645
                    )
646
                  else
647
                    additional_groups = Builtins.add(
×
648
                      additional_groups,
649
                      Item(Id(group), group, false)
650
                    )
651
                  end
652
                end
653
              else
654
                if group == defaultgroup
×
655
                  available_groups = Builtins.add(
×
656
                    available_groups,
657
                    Item(Id(group), group, true)
658
                  )
659
                  defaultgroup_shown = true
×
660
                else
661
                  available_groups = Builtins.add(
×
662
                    available_groups,
663
                    Item(Id(group), group)
664
                  )
665
                end
666
                if Builtins.haskey(groups, group)
×
667
                  additional_groups = Builtins.add(
×
668
                    additional_groups,
669
                    Item(Id(group), group, true)
670
                  )
671
                else
672
                  additional_groups = Builtins.add(
×
673
                    additional_groups,
674
                    Item(Id(group), group, false)
675
                  )
676
                end
677
              end
678
            end
679
          end
680
        end
681
        # show default group, even if the type is 'wrong' (#43433)
682
        if !defaultgroup_shown
×
683
          if Ops.get(all_groupnames, ["local", defaultgroup], 0) == 1 ||
×
684
              Ops.get(all_groupnames, ["system", defaultgroup], 0) == 1
685
            available_groups = Builtins.add(
×
686
              available_groups,
687
              Item(Id(defaultgroup), defaultgroup, true)
688
            )
689
          end
690
        end
691

692
        if defaultgroup == ""
×
693
          available_groups = Builtins.add(
×
694
            available_groups,
695
            # group name is not known (combobox item):
696
            Item(Id(""), _("(Unknown)"), true)
697
          )
698
        end
699

700
        edit_defaultgroup = ComboBox(
×
701
          Id(:defaultgroup),
702
          Opt(:hstretch),
703
          # combobox label
704
          _("De&fault Group"),
705
          available_groups
706
        )
707
        edit_shell = ComboBox(
×
708
          Id(:shell),
709
          Opt(:hstretch, :editable),
710
          # combobox label
711
          _("Login &Shell"),
712
          available_shells
713
        )
714

715
        additional_data = Empty()
×
716
        if user_type == "system" || user_type == "local"
×
717
          additional_data = Top(
×
718
            InputField(
719
              Id(:addd),
720
              Opt(:hstretch),
721
              # textentry label
722
              _("Addi&tional User Information"),
723
              addit_data
724
            )
725
          )
726
        end
727

728
        browse = VBox(
×
729
          VSpacing(1),
730
          PushButton(Id(:browse), Opt(:key_F6), _("B&rowse..."))
731
        )
732

733
        home_w = VBox(
×
734
          InputField(Id(:home), Opt(:hstretch), _("&Home Directory"), home),
735
        )
736
        new_user_term = action != "added" ?
×
737
          VBox() :
738
          VBox(
739
            InputField(
740
              Id(:mode),
741
              Opt(:hstretch),
742
              # textentry label
743
              _("Home Directory &Permission Mode"),
744
              mode
745
            ),
746
            # check box label
747
            HBox(
748
              HSpacing(),
749
              Left(CheckBox(Id(:skel), _("E&mpty Home"), no_skel))
750
            )
751
          )
752

753
        HBox(
×
754
          HSpacing(1),
755
          VBox(
756
            # label
757
            do_not_edit ?
×
758
              Label(
759
                _(
760
                  "For remote users, only additional \ngroup memberships can be changed."
761
                )
762
              ) :
763
              VSpacing(0),
764
            HBox(
765
              text_mode ? Empty() : HSpacing(1),
×
766
              HWeight(
767
                3,
768
                VBox(
769
                  VSpacing(0.5),
770
                  Top(
771
                    InputField(
772
                      Id(:uid),
773
                      Opt(:hstretch),
774
                      # textentry label
775
                      _("User &ID (uid)"),
776
                      Builtins.sformat("%1", uid)
777
                    )
778
                  ),
779
                  Top(
780
                    VBox(
781
                      VSpacing(1),
782
                      HBox(home_w, browse),
783
                      move_to_new_location_checkbox(action, create_home),
784
                      new_user_term,
785
                      HBox(
786
                        HSpacing(),
787
                        Left(CheckBox(Id(:btrfs_subvolume), btrfs_label, btrfs_subvolume))
788
                      ),
789
                    )
790
                  ),
791
                  VSpacing(1),
792
                  additional_data,
793
                  text_mode ? Empty() : HSpacing(1),
×
794
                  Top(edit_shell),
795
                  VStretch()
796
                )
797
              ),
798
              HSpacing(2),
799
              HWeight(
800
                2,
801
                VBox(
802
                  VSpacing(0.5),
803
                  edit_defaultgroup,
804
                  VSpacing(0.5),
805
                  MultiSelectionBox(
806
                    Id(:grouplist),
807
                    # selection box label
808
                    _("Additional Gr&oups"),
809
                    additional_groups
810
                  ),
811
                  user_type == "ldap" ?
×
812
                    MultiSelectionBox(
813
                      Id(:ldapgrouplist),
814
                      # selection box label
815
                      _("&LDAP Groups"),
816
                      additional_ldap_groups
817
                    ) :
818
                    Empty()
819
                )
820
              ),
821
              text_mode ? Empty() : HSpacing(1)
×
822
            ),
823
          ),
824
          HSpacing(1)
825
        )
826
      end
827

828
      # generate contents for Plugins Dialog
829
      get_plugins_term = lambda do
×
830
        plugin_client = Ops.get(clients, 0, "")
×
831
        plugin = Ops.get_string(client2plugin, plugin_client, plugin_client)
×
832

833
        items = []
×
834
        Builtins.foreach(clients) do |cl|
×
835
          summary = WFM.CallFunction(cl, ["Summary", { "what" => "user" }])
×
836
          pl = Ops.get_string(client2plugin, cl, cl)
×
837
          if Ops.is_string?(summary)
×
838
            items = Builtins.add(
×
839
              items,
840
              Item(
841
                Id(cl),
842
                Builtins.contains(Ops.get_list(user, "plugins", []), pl) ?
×
843
                  UI.Glyph(:CheckMark) :
844
                  " ",
845
                summary
846
              )
847
            )
848
          end
849
        end
850
        HBox(
×
851
          HSpacing(0.5),
852
          VBox(
853
            Table(
854
              Id(:table),
855
              Opt(:notify),
856
              Header(
857
                " ",
858
                # table header
859
                _("Plug-In Description")
860
              ),
861
              items
862
            ),
863
            HBox(
864
              PushButton(
865
                Id(:change),
866
                Opt(:key_F3),
867
                # pushbutton label
868
                _("Add or &Remove Plug-In")
869
              ),
870
              # pushbutton label
871
              Right(PushButton(Id(:run), Opt(:key_F6), _("&Launch")))
872
            ),
873
            VSpacing(0.5)
874
          ),
875
          HSpacing(0.5)
876
        )
877
      end
878

879

880
      dialog_labels = {
881
        "add_user"  => {
×
882
          # dialog caption:
883
          "local"  => _("New Local User"),
884
          # dialog caption:
885
          "system" => _("New System User"),
886
          # dialog caption:
887
          "ldap"   => _("New LDAP User")
888
        },
889
        "edit_user" => {
890
          # dialog caption:
891
          "local"  => _("Existing Local User"),
892
          # dialog caption:
893
          "system" => _("Existing System User"),
894
          # dialog caption:
895
          "ldap"   => _("Existing LDAP User"),
896
          # dialog caption:
897
          "nis"    => _("Existing NIS User")
898
        }
899
      }
900

901
      tabs = [
902
        # tab label
903
        Item(Id(:edit), _("Us&er Data"), true),
×
904
        # tab label
905
        Item(Id(:details), _("&Details"))
906
      ]
907

908
      if !do_not_edit && user_type != "ldap"
×
909
        # tab label
910
        tabs = Builtins.add(
×
911
          tabs,
912
          Item(Id(:passwordsettings), _("Pass&word Settings"))
913
        )
914
      end
915

916
      if user["type"] == "local" || user["uid"] == "root"
×
917
        tabs << Item(Id(:authorized_keys), _("SSH Public Keys"))
×
918
      end
919

920
      # Now initialize the list of plugins: we must know now if there is some available.
921
      # UsersPlugins will filter out plugins we cannot use for given type
922
      plugin_clients = UsersPlugins.Apply(
×
923
        "GUIClient",
924
        { "what" => "user", "type" => user_type },
925
        {}
926
      )
927
      # remove empty clients
928
      plugin_clients = Builtins.filter(
×
929
        Convert.convert(
930
          plugin_clients,
931
          :from => "map",
932
          :to   => "map <string, string>"
933
        )
934
      ) { |plugin2, client| client != "" }
×
935
      clients = Builtins.maplist(
×
936
        Convert.convert(
937
          plugin_clients,
938
          :from => "map",
939
          :to   => "map <string, string>"
940
        )
941
      ) do |plugin2, client|
942
        Ops.set(client2plugin, client, plugin2)
×
943
        client
×
944
      end
945
      if clients != []
×
946
        # tab label
947
        tabs = Builtins.add(tabs, Item(Id(:plugins), _("Plu&g-Ins")))
×
948
      end
949

950
      dialog_contents = VBox(
×
951
        DumbTab(
952
          Id(:tabs),
953
          tabs,
954
          ReplacePoint(Id(:tabContents), get_edit_term.call)
955
        )
956
      )
957
      has_tabs = true
×
958
      if !UI.HasSpecialWidget(:DumbTab)
×
959
        has_tabs = false
×
960
        tabbar = HBox()
×
961
        Builtins.foreach(tabs) do |it|
×
962
          label = Ops.get_string(it, 1, "")
×
963
          tabbar = Builtins.add(tabbar, PushButton(Ops.get_term(it, 0) do
×
964
            Id(label)
×
965
          end, label))
966
        end
967
        dialog_contents = VBox(
×
968
          Left(tabbar),
969
          Frame("", ReplacePoint(Id(:tabContents), get_edit_term.call))
970
        )
971
      end
972
      if complex_layout
×
973
        dialog_contents = ReplacePoint(Id(:tabContents), get_edit_term.call)
×
974
        Wizard.SetContents(
×
975
          Ops.get_string(dialog_labels, [what, user_type], ""),
976
          dialog_contents,
977
          EditUserDialogHelp(complex_layout, user_type, what),
978
          GetInstArgs.enable_back,
979
          GetInstArgs.enable_next
980
        )
981
      else
982
        Wizard.SetContentsButtons(
×
983
          Ops.get_string(dialog_labels, [what, user_type], ""),
984
          dialog_contents,
985
          EditUserDialogHelp(complex_layout, user_type, what),
986
          Label.CancelButton,
987
          Label.OKButton
988
        )
989
        Wizard.HideAbortButton
×
990
      end
991

992
      ret = :edit
×
993
      current = nil
×
994
      login_modified = false
×
995
      tabids = [:edit, :details, :passwordsettings, :plugins]
×
996
      ldap_user_defaults = UsersLDAP.GetUserDefaults
×
997

998
      # switch focus to specified tab (after error message) and widget inside
999
      focus_tab = lambda do |tab, widget|
×
1000
        widget = deep_copy(widget)
×
1001
        UI.ChangeWidget(Id(:tabs), :CurrentItem, tab) if has_tabs
×
1002
        UI.SetFocus(Id(widget))
×
1003
        ret = :notnext
×
1004

1005
        nil
×
1006
      end
1007

1008
      # map with id's of confirmed questions
1009
      ui_map = {}
×
1010

1011
      while true
×
1012
        # map returned from Check*UI functions
1013
        error_map = {}
×
1014
        # error message
1015
        error = ""
×
1016

1017
        ret = Convert.to_symbol(UI.UserInput) if current != nil
×
1018

1019
        if (ret == :abort || ret == :cancel) && ReallyAbort() != :abort
×
1020
          ret = :notnext
×
1021
          next
×
1022
        end
1023
        break if Builtins.contains([:abort, :back, :cancel], ret)
×
1024
        ret = :next if ret == :ok
×
1025

1026
        tab = Builtins.contains(tabids, ret)
×
1027
        next if tab && ret == current
×
1028

1029
        # ------------------- handle actions inside the tabs
1030

1031
        # 1. click inside User Data dialog or moving outside of it
1032
        if current == :edit
×
1033
          username = Convert.to_string(UI.QueryWidget(Id(:username), :Value))
×
1034

1035
          # empty username during installation (-> go to next module)
1036
          if username == "" && ret == :next && Users.StartDialog("user_add")
×
1037
            # The user login field is empty, this is allowed if the
1038
            # system is part of a network with (e.g.) NIS user management.
1039
            # yes-no popup headline
1040
            if Popup.YesNoHeadline(
×
1041
                _("Empty User Login"),
1042
                # yes-no popup contents
1043
                _(
1044
                  "Leaving the user name empty only makes sense\n" +
1045
                    "in a network environment with an authentication server.\n" +
1046
                    "Leave it empty?"
1047
                )
1048
              )
1049
              ret = :nextmodule
×
1050
              break
×
1051
            end
1052
            focus_tab.call(current, :username)
×
1053
            next
×
1054
          end
1055

1056
          # now gather user data from dialog
1057
          if user_type == "ldap"
×
1058
            # Form the fullname for LDAP user
1059
            # sn (surname) and cn (fullname) are required attributes,
1060
            # they cannot be empty
1061
            givenname = Convert.to_string(
×
1062
              UI.QueryWidget(Id(:givenname), :Value)
1063
            )
1064
            sn = Convert.to_string(UI.QueryWidget(Id(:sn), :Value))
×
1065

1066
            # create default cn/sn if they are not marked for substitution
1067
            if sn == "" &&
×
1068
                (what == "edit_user" ||
1069
                  !Builtins.haskey(ldap_user_defaults, "sn"))
1070
              if givenname == ""
×
1071
                sn = username
×
1072
              else
1073
                sn = givenname
×
1074
                givenname = ""
×
1075
              end
1076
            end
1077
            # enable changing of cn value only if LDAP user is not saved yet (bnc#904645)
1078
            if (cn == "" || action == "added") &&
×
1079
                # no substitution when editing: TODO bug 238282
1080
                (what == "edit_user" ||
1081
                  !# cn should not be substituted:
1082
                  Builtins.haskey(ldap_user_defaults, "cn"))
1083
              # if 'givenname' or 'sn' should be substituted, wait for it
1084
              # and do not create cn now:
1085
              if !Builtins.haskey(ldap_user_defaults, "sn") &&
×
1086
                  !Builtins.haskey(ldap_user_defaults, "givenName")
1087
                cn = Ops.add(Ops.add(givenname, givenname != "" ? " " : ""), sn)
×
1088
              end
1089
            end
1090
            UI.ChangeWidget(Id(:givenname), :Value, givenname)
×
1091
            UI.ChangeWidget(Id(:sn), :Value, sn)
×
1092
          else
1093
            cn = Convert.to_string(UI.QueryWidget(Id(:cn), :Value))
×
1094
            error = UsersSimple.CheckFullname(cn)
×
1095
            if error != ""
×
1096
              Report.Error(error)
×
1097
              focus_tab.call(current, :cn)
×
1098
              next
×
1099
            end
1100
          end
1101
          if Builtins.haskey(user, "givenName") &&
×
1102
              Ops.is_list?(Ops.get(user, "givenName"))
1103
            Ops.set(user, ["givenName", 0], givenname)
×
1104
          else
1105
            Ops.set(user, "givenName", givenname)
×
1106
          end
1107
          if Builtins.haskey(user, "sn") && Ops.is_list?(Ops.get(user, "sn"))
×
1108
            Ops.set(user, ["sn", 0], sn)
×
1109
          else
1110
            Ops.set(user, "sn", sn)
×
1111
          end
1112
          if Builtins.haskey(user, "cn") && Ops.is_list?(Ops.get(user, "cn"))
×
1113
            Ops.set(user, ["cn", 0], cn)
×
1114
          else
1115
            Ops.set(user, "cn", cn)
×
1116
          end
1117

1118
          # generate a login name from the full name
1119
          # (not for LDAP, there are customized rules...)
1120
          if ret == :cn
×
1121
            uname = Convert.to_string(UI.QueryWidget(Id(:username), :Value))
×
1122
            login_modified = false if login_modified && uname == "" # reenable suggestion
×
1123
            if !login_modified
×
1124
              full_name = UI.QueryWidget(Id(ret), :Value)
×
1125
              username = Y2Users::Username.generate_from(full_name)
×
1126
              UI.ChangeWidget(Id(:username), :Value, username)
×
1127
            end
1128
          end
1129
          login_modified = true if ret == :username
×
1130
          # in continue mode: move to 'User Management' without adding user
1131
          if ret == :additional
×
1132
            if username == "" &&
×
1133
                (user_type == "ldap" && cn == "" && givenname == "" ||
1134
                  user_type != "ldap" && cn == "")
1135
              ret = :nosave
×
1136
            end
1137
          end
1138
        end
1139
        # now check if currently added user data are correct
1140
        # (going out from User Data tab)
1141
        if current == :edit && !do_not_edit &&
×
1142
            (ret == :next || ret == :additional || tab)
1143
          # --------------------------------- username checks, part 1/2
1144
          error = Users.CheckUsername(username)
×
1145
          if error != ""
×
1146
            Report.Error(error)
×
1147
            focus_tab.call(current, :username)
×
1148
            next
×
1149
          end
1150
          Ops.set(user, "uid", username)
×
1151

1152
          # --------------------------------- uid check (for nil value)
1153
          if !tab && uid == nil
×
1154
            error = Users.CheckUID(uid)
×
1155
            if error != ""
×
1156
              Report.Error(error)
×
1157
              focus_tab.call(current, :details)
×
1158
              next
×
1159
            end
1160
          end
1161

1162
          # --------------------------------- password checks
1163
          pw1 = Convert.to_string(UI.QueryWidget(Id(:pw1), :Value))
×
1164
          pw2 = Convert.to_string(UI.QueryWidget(Id(:pw2), :Value))
×
1165

1166
          if pw1 != pw2
×
1167
            # The two user password information do not match
1168
            # error popup
1169
            Report.Error(_("The passwords do not match.\nTry again."))
×
1170
            focus_tab.call(current, :pw1)
×
1171
            next
×
1172
          end
1173
          if (pw1 != "" || !tab) && pw1 != @default_pw
×
1174
            error = UsersSimple.CheckPassword(pw1, user_type)
×
1175
            if error != ""
×
1176
              Report.Error(error)
×
1177
              focus_tab.call(current, :pw1)
×
1178
              next
×
1179
            end
1180

1181
            errors = UsersSimple.CheckPasswordUI(
×
1182
              { "uid" => username, "userPassword" => pw1, "type" => user_type }
1183
            )
1184
            if errors != []
×
1185
              message = Ops.add(
×
1186
                Ops.add(
1187
                  Builtins.mergestring(errors, "\n\n"),
1188
                  # last part of message popup
1189
                  "\n\n"
1190
                ),
1191
                _("Really use this password?")
1192
              )
1193
              if !Popup.YesNo(message)
×
1194
                focus_tab.call(current, :pw1)
×
1195
                next
×
1196
              end
1197
            end
1198
            # now saving plain text password
1199
            if Ops.get_boolean(user, "encrypted", false)
×
1200
              Ops.set(user, "encrypted", false)
×
1201
            end
1202
            Ops.set(user, "userPassword", pw1)
×
1203
            Ops.set(user, "shadowLastChange", Users.LastChangeIsNow)
×
1204
            password = pw1
×
1205
          end
1206

1207
          # build default home dir
1208
          if home == "" || home == default_home ||
×
1209
              Builtins.issubstring(home, "%")
1210
            # LDAP: maybe value of homedirectory should be substituted?
1211
            if user_type == "ldap" && Builtins.issubstring(home, "%")
×
1212
              user = UsersLDAP.SubstituteValues("user", user)
×
1213
              home = Ops.get_string(user, "homeDirectory", default_home)
×
1214
            end
1215
            if home == default_home || home == ""
×
1216
              home = Ops.add(default_home, username)
×
1217
            end
1218
          end
1219
          if ret != :details && username != org_username
×
1220
            generated_home = Ops.add(default_home, username)
×
1221
            if user_type == "ldap" && Builtins.issubstring(default_home, "%")
×
1222
              tmp_user = UsersLDAP.SubstituteValues("user", user)
×
1223
              generated_home = Ops.get_string(tmp_user, "homeDirectory", home)
×
1224
            end
1225
            if home != generated_home &&
×
1226
                (what == "add_user" ||
1227
                  Popup.YesNo(
1228
                    Builtins.sformat(
1229
                      # popup question
1230
                      _("Change home directory to %1?"),
1231
                      generated_home
1232
                    )
1233
                  ))
1234
              home = generated_home
×
1235
            end
1236
          end
1237
          # -------------------------------------- directory checks
1238
          if !tab && home != org_home
×
1239
            error = Users.CheckHome(home)
×
1240
            if error != ""
×
1241
              Report.Error(error)
×
1242
              ret = :notnext
×
1243
              next
×
1244
            end
1245
            failed = false
×
1246
            begin
×
1247
              error_map = Users.CheckHomeUI(uid, home, ui_map)
×
1248
              if error_map != {}
×
1249
                if Ops.get_string(error_map, "question_id", "") == "chown" &&
×
1250
                    !Builtins.haskey(error_map, "owned")
1251
                  ret2 = ask_chown_home(home, chown_home)
×
1252
                  if Ops.get_boolean(ret2, "retval", false)
×
1253
                    Ops.set(ui_map, "chown", home)
×
1254
                    chown_home = Ops.get_boolean(ret2, "chown_home", chown_home)
×
1255
                  else
1256
                    failed = true
×
1257
                  end
1258
                else
1259
                  if !Popup.YesNo(Ops.get_string(error_map, "question", ""))
×
1260
                    failed = true
×
1261
                  else
1262
                    Ops.set(
×
1263
                      ui_map,
1264
                      Ops.get_string(error_map, "question_id", ""),
1265
                      home
1266
                    )
1267
                  end
1268
                end
1269
              end
1270
            end while error_map != {} && !failed
1271

1272
            if failed
×
1273
              ret = :notnext
×
1274
              next
×
1275
            end
1276
          end
1277
          Ops.set(user, "homeDirectory", home)
×
1278
          Ops.set(user, "chown_home", chown_home)
×
1279

1280
          # --------------------------------- username checks, part 2/2
1281
          if what == "add_user" || username != org_username
×
1282
            if AskForUppercasePopup(username) != :ok
×
1283
              focus_tab.call(current, :username)
×
1284
              next
×
1285
            end
1286
          end
1287
          # --------------------------------- autologin (during installation)
1288
          if Users.StartDialog("user_add") && installation
×
1289
            if Autologin.available
×
1290
              Autologin.user = Convert.to_boolean(
×
1291
                UI.QueryWidget(Id(:autologin), :Value)
1292
              ) ? username : ""
1293
              Autologin.used = Convert.to_boolean(
×
1294
                UI.QueryWidget(Id(:autologin), :Value)
1295
              )
1296
              Autologin.modified = true
×
1297
            end
1298
          elsif UI.WidgetExists(Id(:ena))
×
1299
            # -------------------------------------- enable/disable checks
1300

1301
            new_enabled = !Convert.to_boolean(UI.QueryWidget(Id(:ena), :Value))
×
1302
            if enabled != new_enabled
×
1303
              enabled = new_enabled
×
1304
              if enabled
×
1305
                Ops.set(user, "enabled", true)
×
1306
                if Builtins.haskey(user, "disabled")
×
1307
                  Ops.set(user, "disabled", false)
×
1308
                end
1309
              else
1310
                Ops.set(user, "disabled", true)
×
1311
                if Builtins.haskey(user, "enabled")
×
1312
                  Ops.set(user, "enabled", false)
×
1313
                end
1314
              end
1315
            end
1316
          end
1317
          root_mail_checked = Convert.to_boolean(
×
1318
            UI.QueryWidget(Id(:root_mail), :Value)
1319
          )
1320
          root_pw = UI.WidgetExists(Id(:root_pw)) &&
×
1321
            Convert.to_boolean(UI.QueryWidget(Id(:root_pw), :Value))
1322
          # save the username for possible check if it was changed
1323
          # and home directory should be re-generated
1324
          org_username = username if org_username == ""
×
1325
        end
1326

1327
        # inside Details dialog
1328
        if current == :details && ret == :browse
×
1329
          start_dir = Dir.exists?(home) ? home : Users.GetDefaultHome(new_type)
×
1330
          selected_dir = cleanpath(UI.AskForExistingDirectory(start_dir, ""))
×
1331
          UI.ChangeWidget(Id(:home), :Value, selected_dir) unless selected_dir.empty?
×
1332
        end
1333

1334
        # going from Details dialog
1335
        if current == :details && (ret == :next || tab)
×
1336
          new_shell = Convert.to_string(UI.QueryWidget(Id(:shell), :Value))
×
1337
          new_uid = Convert.to_string(UI.QueryWidget(Id(:uid), :Value))
×
1338
          new_defaultgroup = Convert.to_string(
×
1339
            UI.QueryWidget(Id(:defaultgroup), :Value)
1340
          )
1341

1342
          new_home = cleanpath(UI.QueryWidget(Id(:home), :Value))
×
1343

1344
          if what == "add_user"
×
1345
            btrfs_subvolume = UI.QueryWidget(Id(:btrfs_subvolume), :Value)
×
1346
            no_skel = Convert.to_boolean(UI.QueryWidget(Id(:skel), :Value))
×
1347
            mode = Convert.to_string(UI.QueryWidget(Id(:mode), :Value))
×
1348
          end
1349

1350
          if do_not_edit
×
1351
            new_home = home
×
1352
            new_shell = shell
×
1353
            new_uid = Builtins.sformat("%1", uid)
×
1354
            new_defaultgroup = defaultgroup
×
1355
          end
1356
          new_i_uid = Builtins.tointeger(new_uid)
×
1357

1358
          # additional data in GECOS field (passwd only)
1359
          if new_type == "local" || new_type == "system"
×
1360
            addit_data = Convert.to_string(UI.QueryWidget(Id(:addd), :Value))
×
1361
            error2 = Users.CheckGECOS(addit_data)
×
1362
            if error2 != ""
×
1363
              Report.Error(error2)
×
1364
              focus_tab.call(current, :addd)
×
1365
              ret = :notnext
×
1366
              next
×
1367
            end
1368
          end
1369

1370
          # check the uid
1371
          if new_i_uid != uid
×
1372
            error2 = Users.CheckUID(new_i_uid)
×
1373
            if error2 != ""
×
1374
              Report.Error(error2)
×
1375
              focus_tab.call(current, :uid)
×
1376
              next
×
1377
            end
1378
            failed = false
×
1379
            begin
×
1380
              error_map = Users.CheckUIDUI(new_i_uid, ui_map)
×
1381
              if error_map != {}
×
1382
                if !Popup.YesNo(Ops.get_string(error_map, "question", ""))
×
1383
                  focus_tab.call(current, :uid)
×
1384
                  failed = true
×
1385
                else
1386
                  Ops.set(
×
1387
                    ui_map,
1388
                    Ops.get_string(error_map, "question_id", ""),
1389
                    new_i_uid
1390
                  )
1391
                  if Builtins.contains(
×
1392
                      ["local", "system"],
1393
                      Ops.get_string(error_map, "question_id", "")
1394
                    )
1395
                    new_type = Ops.get_string(error_map, "question_id", "local")
×
1396
                    UsersCache.SetUserType(new_type)
×
1397
                  end
1398
                end
1399
              end
1400
            end while error_map != {} && !failed
1401
            if failed
×
1402
              focus_tab.call(current, :uid)
×
1403
              next
×
1404
            end
1405
          end # end of uid checks
1406

1407
          if defaultgroup != new_defaultgroup
×
1408
            g = Users.GetGroupByName(new_defaultgroup, new_type)
×
1409
            g = Users.GetGroupByName(new_defaultgroup, "") if g == {}
×
1410
            gid = GetInt(Ops.get(g, "gidNumber"), gid)
×
1411
          end
1412

1413
          # check the homedirectory
1414
          if home != new_home || what == "add_user"
×
1415
            error2 = Users.CheckHome(new_home)
×
1416
            if error2 != ""
×
1417
              Report.Error(error2)
×
1418
              focus_tab.call(current, :home)
×
1419
              next
×
1420
            end
1421
            if new_home.empty?
×
1422
              Report.Error(_("Home cannot be empty."))
×
1423
              UI.ChangeWidget(Id(:home), :Value, home)
×
1424
              focus_tab.call(current, :home)
×
1425
              next
×
1426
            end
1427
            failed = false
×
1428
            begin
×
1429
              error_map = Users.CheckHomeUI(new_i_uid, new_home, ui_map)
×
1430
              if error_map != {}
×
1431
                if Ops.get_string(error_map, "question_id", "") == "chown" &&
×
1432
                    !Builtins.haskey(error_map, "owned")
1433
                  ret2 = ask_chown_home(new_home, chown_home)
×
1434
                  if Ops.get_boolean(ret2, "retval", false)
×
1435
                    Ops.set(ui_map, "chown", new_home)
×
1436
                    chown_home = Ops.get_boolean(ret2, "chown_home", chown_home)
×
1437
                  else
1438
                    failed = true
×
1439
                  end
1440
                else
1441
                  if !Popup.YesNo(Ops.get_string(error_map, "question", ""))
×
1442
                    failed = true
×
1443
                  else
1444
                    Ops.set(
×
1445
                      ui_map,
1446
                      Ops.get_string(error_map, "question_id", ""),
1447
                      new_home
1448
                    )
1449
                  end
1450
                end
1451
              end
1452
            end while error_map != {} && !failed
1453

1454
            if failed
×
1455
              focus_tab.call(current, :home)
×
1456
              next
×
1457
            end
1458
          end
1459

1460
          error_map = Users.CheckShellUI(new_shell, ui_map)
×
1461
          if error_map != {}
×
1462
            if !Popup.YesNo(Ops.get_string(error_map, "question", ""))
×
1463
              focus_tab.call(current, :shell)
×
1464
              next
×
1465
            else
1466
              Ops.set(
×
1467
                ui_map,
1468
                Ops.get_string(error_map, "question_id", ""),
1469
                new_shell
1470
              )
1471
            end
1472
          end
1473

1474
          # generate new map of groups (NIS groups were not shown!)
1475
          new_groups = Builtins.listmap(
×
1476
            Convert.convert(
1477
              UI.QueryWidget(Id(:grouplist), :SelectedItems),
1478
              :from => "any",
1479
              :to   => "list <string>"
1480
            )
1481
          ) { |g| { g => 1 } }
×
1482
          if new_type == "ldap"
×
1483
            Builtins.foreach(
×
1484
              Convert.convert(
1485
                UI.QueryWidget(Id(:ldapgrouplist), :SelectedItems),
1486
                :from => "any",
1487
                :to   => "list <string>"
1488
              )
1489
            ) { |group| new_groups = Builtins.add(new_groups, group, 1) }
×
1490
          end
1491
          # now add NIS groups again (were not shown in dialog)
1492
          Builtins.foreach(nis_groups) do |group, val|
×
1493
            if !Builtins.haskey(new_groups, group)
×
1494
              new_groups = Builtins.add(new_groups, group, 1)
×
1495
            end
1496
          end
1497
          # TODO remove from local g. when there is nis g. with same name
1498
          if do_not_edit && !grouplist_modified && groups != new_groups
×
1499
            grouplist_modified = true
×
1500
          end
1501
          if new_home == "/var/lib/nobody"
×
1502
            create_home = false
×
1503
            chown_home = false
×
1504
          end
1505
          if UI.WidgetExists(Id(:move_home)) &&
×
1506
              UI.QueryWidget(Id(:move_home), :Value) == false
1507
            create_home = false
×
1508
          end
1509

1510
          # A flag to decide if the Btrfs path validation should be performed, since it is not
1511
          # needed when moving it to other location. The "create_home" above is not reliable because
1512
          # it is "true" **when moving the directory/subvolume**.
1513
          check_btrfs_path = !UI.QueryWidget(Id(:move_home), :Value)
×
1514

1515
          # Check if is a valid path when creating a Btfs subvolume
1516
          if perform_btrfs_validations? && !valid_btrfs_path?(new_home)
×
1517
            Report.Error(
×
1518
              # TRANSLATORS: the error message when user try to create a Btrfs subvolume in a not
1519
              # valid location
1520
              _("Given path is not a valid Btrfs location.\n\n" \
1521
              "Choose another path for the home directory\n" \
1522
              "or uncheck the '%{btrfs_option}' option.") % { btrfs_option: btrfs_label }
1523
            )
1524
            focus_tab.call(current, :home)
×
1525
            next
×
1526
          end
1527

1528
          home = new_home
×
1529
          shell = new_shell
×
1530
          uid = new_i_uid
×
1531
          groups = deep_copy(new_groups)
×
1532
          defaultgroup = new_defaultgroup
×
1533
          user_type = new_type
×
1534
          Ops.set(user, "homeDirectory", new_home)
×
1535
          Ops.set(user, "loginShell", new_shell)
×
1536
          Ops.set(user, "gidNumber", gid)
×
1537
          Ops.set(user, "uidNumber", new_i_uid)
×
1538
          Ops.set(user, "grouplist", new_groups)
×
1539
          Ops.set(user, "groupname", new_defaultgroup)
×
1540
          Ops.set(user, "type", new_type)
×
1541
          Ops.set(user, "create_home", create_home)
×
1542
          Ops.set(user, "chown_home", chown_home)
×
1543
          Ops.set(user, "addit_data", addit_data)
×
1544
          Ops.set(user, "no_skeleton", no_skel)
×
1545
          Ops.set(user, "home_mode", mode)
×
1546
          Ops.set(user, "btrfs_subvolume", btrfs_subvolume)
×
1547
        end
1548

1549
        if current == :passwordsettings && (ret == :next || tab)
×
1550
          exp = Convert.to_string(UI.QueryWidget(Id("shadowExpire"), :Value))
×
1551
          if exp != "" &&
×
1552
              !Builtins.regexpmatch(
1553
                exp,
1554
                "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]"
1555
              )
1556
            # popup text: Don't reorder the letters YYYY-MM-DD!!!
1557
            # The date must stay in this format
1558
            Popup.Message(
×
1559
              _("The expiration date must be in the format YYYY-MM-DD.")
1560
            )
1561
            focus_tab.call(current, "shadowExpire")
×
1562
            next
×
1563
          end
1564

1565
          Builtins.foreach(
×
1566
            ["shadowWarning", "shadowMax", "shadowMin", "shadowInactive"]
1567
          ) do |shadowsymbol|
1568
            if Ops.get(user, shadowsymbol) !=
×
1569
                UI.QueryWidget(Id(shadowsymbol), :Value)
1570
              Ops.set(
×
1571
                user,
1572
                shadowsymbol,
1573
                Builtins.sformat("%1", UI.QueryWidget(Id(shadowsymbol), :Value))
1574
              )
1575
            end
1576
          end
1577

1578
          new_exp_date = Convert.to_string(
×
1579
            UI.QueryWidget(Id("shadowExpire"), :Value)
1580
          )
1581
          if new_exp_date != exp_date
×
1582
            exp_date = new_exp_date
×
1583
            if exp_date == ""
×
1584
              user["shadowExpire"] = ""
×
1585
            else
1586
              out = SCR.Execute(
×
1587
                path(".target.bash_output"),
1588
                "/usr/bin/date --date=#{exp_date.shellescape}' UTC' +%s"
1589
              )
1590
              seconds_s = Builtins.deletechars(
×
1591
                Ops.get_string(out, "stdout", "0"),
1592
                "\n"
1593
              )
1594
              if seconds_s != ""
×
1595
                days = Ops.divide(Builtins.tointeger(seconds_s), 60 * 60 * 24)
×
1596
                Ops.set(user, "shadowExpire", Builtins.sformat("%1", days))
×
1597
              end
1598
            end
1599
          end
1600
          if UI.QueryWidget(Id(:force_pw), :Value) == true
×
1601
            # force password change
1602
            Ops.set(user, "shadowLastChange", "0")
×
1603
          end
1604
        end
1605

1606
        if current == :authorized_keys
×
1607
          handle_authorized_keys_input(ret, user)
×
1608
        end
1609

1610
        # inside plugins dialog
1611
        if current == :plugins
×
1612
          plugin_client = Convert.to_string(
×
1613
            UI.QueryWidget(Id(:table), :CurrentItem)
1614
          )
1615
          if plugin_client != nil
×
1616
            plugin = Ops.get_string(client2plugin, plugin_client, plugin_client)
×
1617
          end
1618

1619
          if ret == :table || ret == :change
×
1620
            ret = Builtins.contains(Ops.get_list(user, "plugins", []), plugin) ? :del : :add
×
1621
          end
1622
          if ret == :del
×
1623
            out = UsersPlugins.Apply(
×
1624
              "PluginRemovable",
1625
              { "what" => "user", "type" => user_type, "plugins" => [plugin] },
1626
              {}
1627
            )
1628
            # check if plugin _could_ be deleted!
1629
            if Builtins.haskey(out, plugin) &&
×
1630
                !Ops.get_boolean(out, plugin, false)
1631
              # popup message
1632
              Popup.Message(_("This plug-in cannot be removed."))
×
1633
              ret = :not_next
×
1634
              next
×
1635
            end
1636
          end
1637
          if ret == :add || ret == :del || ret == :run
×
1638
            # functions for adding/deleting/launching plugin work on
1639
            # Users::user_in_work, so we must update it before
1640
            if what == "edit_user"
×
1641
              Users.EditUser(user)
×
1642
            else
1643
              Users.AddUser(user)
×
1644
            end
1645
          end
1646
          if ret == :add
×
1647
            error = Users.AddUserPlugin(plugin)
×
1648
            if error != ""
×
1649
              Popup.Error(error)
×
1650
              ret = :notnext
×
1651
              next
×
1652
            end
1653
            user = Users.GetCurrentUser
×
1654
            reinit_userdata.call
×
1655
            UI.ChangeWidget(
×
1656
              Id(:table),
1657
              term(:Item, plugin_client, 0),
1658
              UI.Glyph(:CheckMark)
1659
            )
1660
          end
1661
          if ret == :del
×
1662
            error = Users.RemoveUserPlugin(plugin)
×
1663
            if error != ""
×
1664
              Popup.Error(error)
×
1665
              ret = :notnext
×
1666
              next
×
1667
            end
1668
            user = Users.GetCurrentUser
×
1669
            reinit_userdata.call
×
1670
            UI.ChangeWidget(Id(:table), term(:Item, plugin_client, 0), " ")
×
1671
          end
1672
          if ret == :run
×
1673
            plugin_added = false
×
1674
            # first, add the plugin if necessary
1675
            if !Builtins.contains(Ops.get_list(user, "plugins", []), plugin)
×
1676
              error = Users.AddUserPlugin(plugin)
×
1677
              if error != ""
×
1678
                Popup.Error(error)
×
1679
                ret = :notnext
×
1680
                next
×
1681
              end
1682
              plugin_added = true
×
1683
              user = Users.GetCurrentUser
×
1684
              reinit_userdata.call
×
1685
            end
1686
            plugin_ret = WFM.CallFunction(
×
1687
              plugin_client,
1688
              ["Dialog", { "what" => "user" }, user]
1689
            )
1690
            if plugin_ret == :next
×
1691
              # update the map of changed user
1692
              user = Users.GetCurrentUser
×
1693
              reinit_userdata.call
×
1694
              UI.ChangeWidget(
×
1695
                Id(:table),
1696
                term(:Item, plugin_client, 0),
1697
                UI.Glyph(:CheckMark)
1698
              )
1699
            # for `cancel we must remove the plugin if it was added because of `run
1700
            elsif plugin_added
×
1701
              error = Users.RemoveUserPlugin(plugin)
×
1702
              if error != ""
×
1703
                Popup.Error(error)
×
1704
                ret = :notnext
×
1705
                next
×
1706
              end
1707
              user = Users.GetCurrentUser
×
1708
              reinit_userdata.call
×
1709
            end
1710
          end
1711
        end
1712

1713
        # ------------------- now handle switching between the tabs
1714
        if ret == :edit
×
1715
          Wizard.SetHelpText(EditUserDialogHelp(installation, user_type, what))
×
1716
          UI.ReplaceWidget(:tabContents, get_edit_term.call)
×
1717

1718
          # update the contents of User Data Dialog
1719
          if do_not_edit
×
1720
            UI.ChangeWidget(Id(:cn), :Enabled, false)
×
1721
            UI.ChangeWidget(Id(:username), :Enabled, false)
×
1722
            UI.ChangeWidget(Id(:pw1), :Enabled, false)
×
1723
            UI.ChangeWidget(Id(:pw2), :Enabled, false)
×
1724
          end
1725
          if what == "add_user"
×
1726
            if user_type == "ldap"
×
1727
              UI.SetFocus(Id(:givenname))
×
1728
            else
1729
              UI.SetFocus(Id(:cn))
×
1730
            end
1731
          end
1732
          if password != nil || what == "edit_user"
×
1733
            UI.ChangeWidget(Id(:pw1), :Value, @default_pw)
×
1734
            UI.ChangeWidget(Id(:pw2), :Value, @default_pw)
×
1735
          end
1736
          if complex_layout && !Autologin.available
×
1737
            UI.ChangeWidget(Id(:autologin), :Enabled, false)
×
1738
            UI.ChangeWidget(Id(:autologin), :Value, false)
×
1739
          end
1740

1741
          # LDAP users can be disabled only with certain plugins (bnc#557714)
1742
          if UI.WidgetExists(Id(:ena)) && user_type == "ldap"
×
1743
            ena = Builtins.contains(
×
1744
              Ops.get_list(user, "plugins", []),
1745
              "UsersPluginLDAPShadowAccount"
1746
            ) ||
1747
              Builtins.contains(
1748
                Ops.get_list(user, "plugins", []),
1749
                "UsersPluginLDAPPasswordPolicy"
1750
              )
1751
            UI.ChangeWidget(Id(:ena), :Enabled, ena)
×
1752
          end
1753

1754
          current = ret
×
1755
        end
1756
        if ret == :details
×
1757
          UI.ReplaceWidget(:tabContents, get_details_term.call)
×
1758
          Wizard.SetHelpText(EditUserDetailsDialogHelp(user_type, what))
×
1759

1760
          UI.ChangeWidget(Id(:btrfs_subvolume), :Enabled, Mode.config || btrfs_available?)
×
1761
          if what == "edit_user"
×
1762
            if user["org_user"] && !Mode.config
×
1763
              # User has already been created in the installed system. So btrfs_subvolume cannot be changed
1764
              UI.ChangeWidget(Id(:btrfs_subvolume), :Enabled, false)
×
1765
              # Show the value for the current home directory/subvolume
1766
              UI.ChangeWidget(Id(:btrfs_subvolume), :Value, btrfs_subvolume?(org_home))
×
1767
            else
1768
              UI.ChangeWidget(Id(:btrfs_subvolume), :Value, user["btrfs_subvolume"])
×
1769
            end
1770
          end
1771

1772
          if do_not_edit
×
1773
            [:uid, :home, :move_home, :shell, :defaultgroup, :browse, :btrfs_subvolume].each do |widget|
×
1774
              UI.ChangeWidget(Id(widget), :Enabled, false)
×
1775
            end
1776
          end
1777
          if user_type == "ldap" && !Ldap.file_server
×
1778
            UI.ChangeWidget(Id(:browse), :Enabled, false)
×
1779
            if UI.WidgetExists(Id(:move_home))
×
1780
              UI.ChangeWidget(Id(:move_home), :Enabled, false)
×
1781
            end
1782
          end
1783
          if !FileUtils.Exists(home) && UI.WidgetExists(Id(:move_home))
×
1784
            UI.ChangeWidget(Id(:move_home), :Enabled, false)
×
1785
          end
1786
          if UI.WidgetExists(Id(:mode))
×
1787
            UI.ChangeWidget(Id(:mode), :ValidChars, "01234567")
×
1788
            UI.ChangeWidget(Id(:mode), :InputMaxLength, 3)
×
1789
          end
1790
          UI.ChangeWidget(Id(:shell), :Value, shell)
×
1791

1792
          current = ret
×
1793
        end
1794
        if ret == :passwordsettings
×
1795
          # get_password_term may modify exp_date!
1796
          UI.ReplaceWidget(:tabContents, get_password_term(user, exp_date))
×
1797
          if GetString(Ops.get(user, "shadowLastChange"), "0") == "0"
×
1798
            # forcing password change cannot be undone
1799
            UI.ChangeWidget(Id(:force_pw), :Enabled, false)
×
1800
          end
1801
          Wizard.SetHelpText(EditUserPasswordDialogHelp())
×
1802
          current = ret
×
1803
        end
1804
        if ret == :authorized_keys
×
1805
          display_authorized_keys_tab(user)
×
1806
          Wizard.SetHelpText(EditAuthorizedKeysDialogHelp())
×
1807
          current = ret
×
1808
        end
1809
        if ret == :plugins
×
1810
          UI.ReplaceWidget(:tabContents, get_plugins_term.call)
×
1811
          Wizard.SetHelpText(PluginDialogHelp())
×
1812
          UI.ChangeWidget(Id(:table), :CurrentItem, plugin_client)
×
1813
          current = ret
×
1814
        end
1815

1816
        if (ret == :next || ret == :additional) &&
×
1817
            # for do_not_edit, there may be a change in groups (Details dialog)
1818
            (!do_not_edit || grouplist_modified)
1819
          # --------------------------------- final check
1820
          error = Users.CheckUser(user)
×
1821
          if error != ""
×
1822
            Report.Error(error)
×
1823
            ret = :notnext
×
1824
            next
×
1825
          end
1826

1827
          # --------------------------------- save the settings
1828
          if Builtins.haskey(user, "check_error")
×
1829
            user = Builtins.remove(user, "check_error")
×
1830
          end
1831
          if what == "edit_user"
×
1832
            error_msg = Users.EditUser(user)
×
1833
          else
1834
            error_msg = Users.AddUser(user)
×
1835
          end
1836
          if error_msg != ""
×
1837
            Report.Error(error_msg)
×
1838
            ret = :notnext
×
1839
            next
×
1840
          end
1841
          # check if autologin is not set for some user
1842
          if what == "add_user" && !complex_layout &&
×
1843
              # ask only when there is still one user (bnc#332729)
1844
              Builtins.size(UsersCache.GetUsernames("local")) == 1
1845
            Autologin.AskForDisabling(
×
1846
              # popup text
1847
              _("Now you have added a new user.")
1848
            )
1849
          end
1850
          if root_mail_checked
×
1851
            Users.RemoveRootAlias(org_username) if username != org_username
×
1852
            Users.AddRootAlias(username)
×
1853
          elsif root_mail # not checked now, but checked before
×
1854
            if username != org_username
×
1855
              Users.RemoveRootAlias(org_username)
×
1856
            else
1857
              Users.RemoveRootAlias(username)
×
1858
            end
1859
          end
1860
          if username == "root"
×
1861
            Users.SaveRootPassword(false)
×
1862
          elsif root_pw
×
1863
            # set root's password
1864
            Users.SetRootPassword(password)
×
1865
            Users.SaveRootPassword(true)
×
1866
          end
1867
        end
1868

1869
        if Builtins.contains(
×
1870
            [:next, :abort, :back, :cancel, :additional, :nosave],
1871
            ret
1872
          )
1873
          break
×
1874
        end
1875
      end
1876

1877
      if ret == :additional || ret == :nosave
×
1878
        # during installation, store the data of first user
1879
        # (to show it when clicking `back from Summary dialog)
1880
        Users.SaveCurrentUser
×
1881
        Users.SetStartDialog("users")
×
1882
      end
1883
      ret
×
1884
    end
1885

1886
    def move_to_new_location_checkbox(action, checked)
1✔
1887
      return Empty() if action != "edited"
×
1888

1889
      HBox(
×
1890
        HSpacing(),
1891
        Left(
1892
          CheckBox(Id(:move_home), _("&Move to New Location"), checked)
1893
        )
1894
      )
1895
    end
1896

1897
    def btrfs_label
1✔
1898
      # TRANSLATORS: label for the checkbox that allows to create the user home directory as a
1899
      # Btrfs subvolume
1900
      _("Create as Btrfs Subvolume")
×
1901
    end
1902

1903
    # Returns clean path
1904
    #
1905
    # Also useful to remove the trailing backslash
1906
    #
1907
    # @param [String] path
1908
    #
1909
    # @return [String] a string path representation or empty string
1910
    def cleanpath(path)
1✔
1911
      return "" if path.nil? || path.empty?
4✔
1912

1913
      Pathname.new(path).cleanpath.to_s
1✔
1914
    rescue TypeError
1915
      ""
×
1916
    end
1917

1918
    # Check if given path is in a btrfs filesystem
1919
    #
1920
    # @param [String, Pathname] path
1921
    #
1922
    # @return [Boolean] true when is a path in a Btrfs filesystem; false otherwise
1923
    def valid_btrfs_path?(path)
1✔
1924
      dirname = Pathname.new(path).dirname
2✔
1925
      fstype = Yast::Execute.locally!.stdout("/usr/bin/stat", "-f", "--format", "%T", dirname).chomp
2✔
1926

1927
      fstype == "btrfs"
2✔
1928
    end
1929

1930
    # Determine if is necessary to check the home path as a valid Btrfs location
1931
    #
1932
    # @return [false] if running in AutoYaST configuration mode
1933
    # @return [false] if the home directory is being moved
1934
    # @return [Boolean] true when the btrfs_subvolume option is present and selected; false otherwise
1935
    def perform_btrfs_validations?
1✔
1936
      return false if Mode.config
×
1937
      return false if UI.QueryWidget(Id(:move_home), :Value)
×
1938

1939
      UI.QueryWidget(Id(:btrfs_subvolume), :Value)
×
1940
    end
1941

1942
    # Whether there is a Btrfs filesystem present
1943
    #
1944
    # @return [Boolean] true if a Btrfs filesystem is found; false otherwise
1945
    def btrfs_available?
1✔
1946
      available_filesystems = Yast::Execute.locally!.stdout(
2✔
1947
        ["/usr/bin/df", "--output=fstype"],
1948
        ["/usr/bin/tail", "-n", "+2"]
1949
      ).split("\n")
1950

1951
      available_filesystems.include?("btrfs")
2✔
1952
    end
1953

1954
    # Whether given path is a Btrfs subvolume
1955
    #
1956
    # @param [String, Pathname] path
1957
    #
1958
    # @return [Boolean] true when is a Btrfs subvolume; false otherwise
1959
    def btrfs_subvolume?(path)
1✔
1960
      return false if path.to_s.empty?
3✔
1961

1962
      !Yast::Execute.locally!.stdout("/usr/sbin/btrfs", "subvolume", "show", path).empty?
2✔
1963
    end
1964

1965
    # Dialog for adding/editing group
1966
    # @param [String] what "add_group" or "edit_group"
1967
    # @return [Symbol] for wizard sequencer
1968
    def EditGroupDialog(what)
1✔
1969
      # create a local copy of current group
1970
      group = Users.GetCurrentGroup
×
1971
      groupname = Ops.get_string(group, "cn", "")
×
1972
      gid = GetInt(Ops.get(group, "gidNumber"), -1)
×
1973
      # these are the users with this group as a default:
1974
      more_users = Ops.get_map(group, "more_users", {})
×
1975
      # these are users from /etc/group:
1976
      userlist = Ops.get_map(group, "userlist", {})
×
1977
      group_type = Ops.get_string(group, "type", "")
×
1978
      new_type = group_type
×
1979
      additional_users = []
×
1980
      member_attribute = UsersLDAP.GetMemberAttribute
×
1981

1982
      if group_type == "ldap"
×
1983
        userlist = Ops.get_map(group, member_attribute, {})
×
1984
      end
1985

1986
      more = Ops.greater_than(Builtins.size(more_users), 0)
×
1987

1988
      dialog_labels = {
1989
        "add_group"  => {
×
1990
          # dialog caption:
1991
          "local"  => _("New Local Group"),
1992
          # dialog caption:
1993
          "system" => _("New System Group"),
1994
          # dialog caption:
1995
          "ldap"   => _("New LDAP Group")
1996
        },
1997
        "edit_group" => {
1998
          # dialog caption:
1999
          "local"  => _("Existing Local Group"),
2000
          # dialog caption:
2001
          "system" => _("Existing System Group"),
2002
          # dialog caption:
2003
          "ldap"   => _("Existing LDAP Group")
2004
        }
2005
      }
2006

2007
      plugin_client = ""
×
2008
      plugin = ""
×
2009
      client2plugin = {}
×
2010
      clients = []
×
2011

2012
      # initialize local variables with current state of group
2013
      reinit_groupdata = lambda do
×
2014
        groupname = Ops.get_string(group, "cn", groupname)
×
2015
        gid = GetInt(Ops.get(group, "gidNumber"), gid)
×
2016
        more_users = Convert.convert(
×
2017
          Ops.get(group, "more_users", more_users),
2018
          :from => "any",
2019
          :to   => "map <string, any>"
2020
        )
2021
        userlist = Convert.convert(
×
2022
          Ops.get(group, "userlist", userlist),
2023
          :from => "any",
2024
          :to   => "map <string, any>"
2025
        )
2026
        group_type = Ops.get_string(group, "type", group_type)
×
2027
        if group_type == "ldap"
×
2028
          userlist = Ops.get_map(group, member_attribute, {})
×
2029
        end
2030

2031
        nil
×
2032
      end
2033

2034
      # generate contents for Group Data Dialog
2035
      get_edit_term = lambda do
×
2036
        i = 0
×
2037
        more_users_items = []
×
2038
        Builtins.foreach(more_users) do |u, val|
×
2039
          if Ops.less_than(i, 42)
×
2040
            more_users_items = Builtins.add(
×
2041
              more_users_items,
2042
              Item(Id(u), u, true)
2043
            )
2044
          end
2045
          if i == 42
×
2046
            more_users_items = Builtins.add(
×
2047
              more_users_items,
2048
              Item(Id("-"), "...", false)
2049
            )
2050
          end
2051
          i = Ops.add(i, 1)
×
2052
        end
2053

2054
        HBox(
×
2055
          HWeight(
2056
            1,
2057
            VBox(
2058
              VSpacing(1),
2059
              Top(
2060
                InputField(
2061
                  Id(:groupname),
2062
                  Opt(:hstretch),
2063
                  # textentry label
2064
                  _("Group &Name"),
2065
                  groupname
2066
                )
2067
              ),
2068
              Top(
2069
                InputField(
2070
                  Id(:gid),
2071
                  Opt(:hstretch),
2072
                  # textentry label
2073
                  _("Group &ID (gid)"),
2074
                  Builtins.sformat("%1", gid)
2075
                )
2076
              ),
2077
              VSpacing(1)
2078
            )
2079
          ),
2080
          HSpacing(2),
2081
          HWeight(
2082
            1,
2083
            VBox(
2084
              VSpacing(1),
2085
              ReplacePoint(
2086
                Id(:rpuserlist),
2087
                # selection box label
2088
                MultiSelectionBox(Id(:userlist), _("Group &Members"), [])
2089
              ),
2090
              more ? VSpacing(1) : VSpacing(0),
×
2091
              more ?
×
2092
                MultiSelectionBox(Id(:more_users), "", more_users_items) :
2093
                VSpacing(0),
2094
              VSpacing(1)
2095
            )
2096
          )
2097
        )
2098
      end
2099

2100
      # generate contents for Plugins Dialog
2101
      get_plugins_term = lambda do
×
2102
        plugin_client = Ops.get(clients, 0, "")
×
2103
        plugin = Ops.get_string(client2plugin, plugin_client, plugin_client)
×
2104

2105
        items = []
×
2106
        Builtins.foreach(clients) do |cl|
×
2107
          summary = WFM.CallFunction(cl, ["Summary", { "what" => "group" }])
×
2108
          pl = Ops.get_string(client2plugin, cl, cl)
×
2109
          if Ops.is_string?(summary)
×
2110
            items = Builtins.add(
×
2111
              items,
2112
              Item(
2113
                Id(cl),
2114
                Builtins.contains(Ops.get_list(group, "plugins", []), pl) ?
×
2115
                  UI.Glyph(:CheckMark) :
2116
                  " ",
2117
                summary
2118
              )
2119
            )
2120
          end
2121
        end
2122
        HBox(
×
2123
          HSpacing(0.5),
2124
          VBox(
2125
            Table(
2126
              Id(:table),
2127
              Opt(:notify),
2128
              Header(
2129
                " ",
2130
                # table header
2131
                _("Plug-In Description")
2132
              ),
2133
              items
2134
            ),
2135
            HBox(
2136
              PushButton(
2137
                Id(:change),
2138
                Opt(:key_F3),
2139
                # pushbutton label
2140
                _("Add or &Remove Plug-In")
2141
              ),
2142
              # pushbutton label
2143
              Right(PushButton(Id(:run), Opt(:key_F6), _("&Launch")))
2144
            ),
2145
            VSpacing(0.5)
2146
          ),
2147
          HSpacing(0.5)
2148
        )
2149
      end
2150

2151
      tabs = []
×
2152
      dialog_contents = Empty()
×
2153

2154
      # Now initialize the list of plugins: we must know now if there is some available.
2155
      # UsersPlugins will filter out plugins we cannot use for given type
2156
      plugin_clients = UsersPlugins.Apply(
×
2157
        "GUIClient",
2158
        { "what" => "group", "type" => group_type },
2159
        {}
2160
      )
2161
      # remove empty clients
2162
      plugin_clients = Builtins.filter(
×
2163
        Convert.convert(
2164
          plugin_clients,
2165
          :from => "map",
2166
          :to   => "map <string, string>"
2167
        )
2168
      ) { |plugin2, client| client != "" }
×
2169
      clients = Builtins.maplist(
×
2170
        Convert.convert(
2171
          plugin_clients,
2172
          :from => "map",
2173
          :to   => "map <string, string>"
2174
        )
2175
      ) do |plugin2, client|
2176
        Ops.set(client2plugin, client, plugin2)
×
2177
        client
×
2178
      end
2179
      use_tabs = Ops.greater_than(Builtins.size(clients), 0)
×
2180
      has_tabs = true
×
2181

2182
      if use_tabs
×
2183
        tabs = [
2184
          # tab label
2185
          Item(Id(:edit), _("Group &Data"), true),
×
2186
          # tab label
2187
          Item(Id(:plugins), _("Plu&g-Ins"))
2188
        ]
2189

2190
        dialog_contents = VBox(
×
2191
          DumbTab(
2192
            Id(:tabs),
2193
            tabs,
2194
            ReplacePoint(Id(:tabContents), get_edit_term.call)
2195
          )
2196
        )
2197
        if !UI.HasSpecialWidget(:DumbTab)
×
2198
          has_tabs = false
×
2199
          tabbar = HBox()
×
2200
          Builtins.foreach(tabs) do |it|
×
2201
            label = Ops.get_string(it, 1, "")
×
2202
            tabbar = Builtins.add(
×
2203
              tabbar,
2204
              PushButton(Ops.get_term(it, 0) { Id(label) }, label)
×
2205
            )
2206
          end
2207
          dialog_contents = VBox(
×
2208
            Left(tabbar),
2209
            Frame("", ReplacePoint(Id(:tabContents), get_edit_term.call))
2210
          )
2211
        end
2212
      else
2213
        dialog_contents = get_edit_term.call
×
2214
      end
2215

2216
      Wizard.SetContentsButtons(
×
2217
        Ops.get_string(dialog_labels, [what, group_type], ""),
2218
        dialog_contents,
2219
        EditGroupDialogHelp(more),
2220
        Label.CancelButton,
2221
        Label.OKButton
2222
      )
2223
      Wizard.HideAbortButton
×
2224

2225
      ret = :edit
×
2226
      current = nil
×
2227
      tabids = [:edit, :plugins]
×
2228

2229
      # switch focus to specified tab (after error message) and widget inside
2230
      focus_tab = lambda do |tab, widget|
×
2231
        widget = deep_copy(widget)
×
2232
        UI.ChangeWidget(Id(:tabs), :CurrentItem, tab) if use_tabs && has_tabs
×
2233
        UI.SetFocus(Id(widget))
×
2234
        ret = :notnext
×
2235

2236
        nil
×
2237
      end
2238
      begin
×
2239
        # map returned from Check*UI functions
2240
        error_map = {}
×
2241
        # map with id's of confirmed questions
2242
        ui_map = {}
×
2243
        # error message
2244
        error = ""
×
2245

2246
        ret = Convert.to_symbol(UI.UserInput) if current != nil
×
2247

2248
        if (ret == :abort || ret == :cancel) && ReallyAbort() != :abort
×
2249
          ret = :notnext
×
2250
          next
×
2251
        end
2252
        break if Builtins.contains([:abort, :back, :cancel], ret)
×
2253

2254
        tab = Builtins.contains(tabids, ret)
×
2255
        next if tab && ret == current
×
2256

2257
        # 1. click inside Group Data dialog or moving outside of it
2258
        if current == :edit && (ret == :next || tab)
×
2259
          new_gid = Convert.to_string(UI.QueryWidget(Id(:gid), :Value))
×
2260
          new_i_gid = Builtins.tointeger(new_gid)
×
2261
          new_groupname = Convert.to_string(
×
2262
            UI.QueryWidget(Id(:groupname), :Value)
2263
          )
2264

2265
          # --------------------------------- groupname checks
2266
          error2 = Users.CheckGroupname(new_groupname)
×
2267
          if error2 != ""
×
2268
            Report.Error(error2)
×
2269
            focus_tab.call(current, :groupname)
×
2270
            next
×
2271
          end
2272

2273
          # --------------------------------- gid checks
2274
          if new_i_gid != gid
×
2275
            error2 = Users.CheckGID(new_i_gid)
×
2276
            if error2 != ""
×
2277
              Report.Error(error2)
×
2278
              focus_tab.call(current, :gid)
×
2279
              next
×
2280
            end
2281
            failed = false
×
2282
            begin
×
2283
              error_map = Users.CheckGIDUI(new_i_gid, ui_map)
×
2284
              if error_map != {}
×
2285
                if !Popup.YesNo(Ops.get_string(error_map, "question", ""))
×
2286
                  failed = true
×
2287
                else
2288
                  Ops.set(
×
2289
                    ui_map,
2290
                    Ops.get_string(error_map, "question_id", ""),
2291
                    new_i_gid
2292
                  )
2293
                  if Builtins.contains(
×
2294
                      ["local", "system"],
2295
                      Ops.get_string(error_map, "question_id", "")
2296
                    )
2297
                    new_type = Ops.get_string(error_map, "question_id", "local")
×
2298
                    UsersCache.SetGroupType(new_type)
×
2299
                  end
2300
                end
2301
              end
2302
            end while error_map != {} && !failed
2303
            if failed
×
2304
              focus_tab.call(current, :gid)
×
2305
              next
×
2306
            end
2307
          end
2308

2309
          # --------------------------------- update userlist
2310
          new_userlist = Builtins.listmap(
×
2311
            Convert.convert(
2312
              UI.QueryWidget(Id(:userlist), :SelectedItems),
2313
              :from => "any",
2314
              :to   => "list <string>"
2315
            )
2316
          ) { |user| { user => 1 } }
×
2317

2318
          # --------------------------------- now everything should be OK
2319
          Ops.set(group, "cn", new_groupname)
×
2320
          Ops.set(group, "more_users", more_users)
×
2321
          Ops.set(group, "gidNumber", new_i_gid)
×
2322
          Ops.set(group, "type", new_type)
×
2323
          if group_type == "ldap"
×
2324
            Ops.set(group, member_attribute, new_userlist)
×
2325
          else
2326
            Ops.set(group, "userlist", new_userlist)
×
2327
          end
2328
          reinit_groupdata.call
×
2329
        end
2330

2331
        # inside plugins dialog
2332
        if current == :plugins
×
2333
          plugin_client = Convert.to_string(
×
2334
            UI.QueryWidget(Id(:table), :CurrentItem)
2335
          )
2336
          if plugin_client != nil
×
2337
            plugin = Ops.get_string(client2plugin, plugin_client, plugin_client)
×
2338
          end
2339
          if ret == :table || ret == :change
×
2340
            ret = Builtins.contains(Ops.get_list(group, "plugins", []), plugin) ? :del : :add
×
2341
          end
2342
          if ret == :del
×
2343
            out = UsersPlugins.Apply(
×
2344
              "PluginRemovable",
2345
              { "what" => "group", "type" => group_type, "plugins" => [plugin] },
2346
              {}
2347
            )
2348
            # check if plugin _could_ be deleted!
2349
            if Builtins.haskey(out, plugin) &&
×
2350
                !Ops.get_boolean(out, plugin, false)
2351
              # popup message
2352
              Popup.Message(_("This plug-in cannot be removed."))
×
2353
              ret = :not_next
×
2354
              next
×
2355
            end
2356
          end
2357
          if ret == :add || ret == :del || ret == :run
×
2358
            # functions for adding/deleting/launching plugin work on
2359
            # Users::group_in_work, so we must update it before
2360
            if what == "edit_group"
×
2361
              Users.EditGroup(group)
×
2362
            else
2363
              Users.AddGroup(group)
×
2364
            end
2365
          end
2366
          if ret == :add
×
2367
            error = Users.AddGroupPlugin(plugin)
×
2368
            if error != ""
×
2369
              Popup.Error(error)
×
2370
              ret = :notnext
×
2371
              next
×
2372
            end
2373
            group = Users.GetCurrentGroup
×
2374
            reinit_groupdata.call
×
2375
            UI.ChangeWidget(
×
2376
              Id(:table),
2377
              term(:Item, plugin_client, 0),
2378
              UI.Glyph(:CheckMark)
2379
            )
2380
          end
2381
          if ret == :del
×
2382
            error = Users.RemoveGroupPlugin(plugin)
×
2383
            if error != ""
×
2384
              Popup.Error(error)
×
2385
              ret = :notnext
×
2386
              next
×
2387
            end
2388
            group = Users.GetCurrentGroup
×
2389
            reinit_groupdata.call
×
2390
            UI.ChangeWidget(Id(:table), term(:Item, plugin_client, 0), " ")
×
2391
          end
2392
          if ret == :run
×
2393
            plugin_added = false
×
2394
            # first, add the plugin if necessary
2395
            if !Builtins.contains(Ops.get_list(group, "plugins", []), plugin)
×
2396
              error = Users.AddGroupPlugin(plugin)
×
2397
              if error != ""
×
2398
                Popup.Error(error)
×
2399
                ret = :notnext
×
2400
                next
×
2401
              end
2402
              plugin_added = true
×
2403
              group = Users.GetCurrentGroup
×
2404
              reinit_groupdata.call
×
2405
            end
2406
            plugin_ret = WFM.CallFunction(
×
2407
              plugin_client,
2408
              ["Dialog", { "what" => "group" }, group]
2409
            )
2410
            if plugin_ret == :next
×
2411
              # update the map of changed group
2412
              group = Users.GetCurrentGroup
×
2413
              reinit_groupdata.call
×
2414
              UI.ChangeWidget(
×
2415
                Id(:table),
2416
                term(:Item, plugin_client, 0),
2417
                UI.Glyph(:CheckMark)
2418
              )
2419
            elsif plugin_added
×
2420
              error = Users.RemoveGroupPlugin(plugin)
×
2421
              if error != ""
×
2422
                Popup.Error(error)
×
2423
                ret = :notnext
×
2424
                next
×
2425
              end
2426
              group = Users.GetCurrentGroup
×
2427
              reinit_groupdata.call
×
2428
            end
2429
          end
2430
        end
2431

2432
        # initialize Edit Group tab
2433
        if ret == :edit
×
2434
          if use_tabs
×
2435
            Wizard.SetHelpText(EditGroupDialogHelp(more))
×
2436
            UI.ReplaceWidget(:tabContents, get_edit_term.call)
×
2437
          end
2438

2439
          UI.SetFocus(Id(:groupname)) if what == "add_group"
×
2440

2441
          if more
×
2442
            # set of users having this group as default - cannot be edited!
2443
            UI.ChangeWidget(Id(:more_users), :Enabled, false)
×
2444
          end
2445
          additional_users = UsersCache.BuildAdditional(group)
×
2446

2447
          # add items later (when there is a huge amount of them, it takes
2448
          # long time to display, so display at least the rest of the dialog)
2449
          if Ops.greater_than(Builtins.size(additional_users), 0)
×
2450
            UI.ReplaceWidget(
×
2451
              Id(:rpuserlist),
2452
              MultiSelectionBox(
2453
                Id(:userlist),
2454
                _("Group &Members"),
2455
                additional_users
2456
              )
2457
            )
2458
          end
2459
          current = ret
×
2460
        end
2461

2462
        if ret == :plugins
×
2463
          UI.ReplaceWidget(:tabContents, get_plugins_term.call)
×
2464
          Wizard.SetHelpText(PluginDialogHelp())
×
2465
          UI.ChangeWidget(Id(:table), :CurrentItem, plugin_client)
×
2466
          current = ret
×
2467
        end
2468

2469
        # save the changes
2470
        if ret == :next
×
2471
          error = Users.CheckGroup(group)
×
2472
          if error != ""
×
2473
            Report.Error(error)
×
2474
            ret = :notnext
×
2475
            next
×
2476
          end
2477
          if what == "edit_group"
×
2478
            error = Users.EditGroup(group)
×
2479
          else
2480
            error = Users.AddGroup(group)
×
2481
          end
2482
          if error != ""
×
2483
            Report.Error(error)
×
2484
            ret = :notnext
×
2485
            next
×
2486
          end
2487
        end
2488
      end until Builtins.contains([:next, :abort, :back, :cancel], ret)
2489
      ret
×
2490
    end
2491

2492
    # Just giving paramaters for committing user
2493
    # @return [Symbol] for wizard sequencer
2494
    def UserSave
1✔
2495
      Users.CommitUser
×
2496
      # adding only one user during install
2497
      if installation && Users.StartDialog("user_add")
×
2498
        return :save
×
2499
      else
2500
        return :next
×
2501
      end
2502
    end
2503

2504
    # Check the group parameters and commit it if all is OK
2505
    # @return [Symbol] for wizard sequencer
2506
    def GroupSave
1✔
2507
      group = Users.GetCurrentGroup
×
2508
      # do not check group which should be deleted
2509
      if Ops.get_string(group, "what", "") != "delete_group"
×
2510
        error = Users.CheckGroup(group)
×
2511
        if error != ""
×
2512
          Report.Error(error)
×
2513
          return :back
×
2514
        end
2515
      end
2516
      Users.CommitGroup
×
2517
      :next
×
2518
    end
2519

2520
    # Handles authorized keys list events
2521
    #
2522
    # @param action [Symbol] Action to handle (:add_authorized_key and :remove_authorized_key)
2523
    # @param user   [Hash] User to update
2524
    def handle_authorized_keys_input(action, user)
1✔
2525
      case action
3✔
2526
      when :add_authorized_key
2527
        add_authorized_key(user)
2✔
2528
      when :remove_authorized_key
2529
        remove_authorized_key(user)
1✔
2530
      end
2531
    end
2532

2533
    # Adds an authorized key to the list
2534
    #
2535
    # @note This method drives the UI and handles error conditions
2536
    #
2537
    # @param user [Hash] User to update
2538
    def add_authorized_key(user)
1✔
2539
      key = read_public_key
2✔
2540
      return if key.nil?
2✔
2541
      if user.fetch("authorized_keys", []).include?(key.to_s)
2✔
2542
        # TRANSLATORS: this error happens when the selected public key is a duplicated
2543
        # (already present in the list of public keys)
2544
        Yast2::Popup.show(
1✔
2545
          _("The selected public key is already present in the list."), headline: :error
2546
        )
2547
        return
1✔
2548
      end
2549

2550
      user["authorized_keys"] ||= []
1✔
2551
      user["authorized_keys"] << key.to_s
1✔
2552
      display_authorized_keys_tab(user)
1✔
2553
    end
2554

2555
    # Asks for the path and retrieves the public key
2556
    def read_public_key
1✔
2557
      # TRANSLATORS: title of the dialog to select a public key to be used when logging
2558
      # via SSH
2559
      path = Yast::UI.AskForExistingFile("", "*.pub", _("Select a public key"))
4✔
2560
      return if path.nil?
4✔
2561
      Y2Users::SSHPublicKey.new(File.read(path))
3✔
2562
    rescue Y2Users::SSHPublicKey::InvalidKey
2563
      # TRANSLATORS: this error happens when the file selected by the user is not a valid public
2564
      # key
2565
      Yast2::Popup.show(
1✔
2566
        _("The selected file does not contain a valid public key"), headline: :error
2567
      )
2568
    rescue Errno::ENOENT
2569
      # TRANSLATORS: this error happens when the user selected a file that has just been removed
2570
      # (the file selector may contain outdated information)
2571
      Yast2::Popup.show(
1✔
2572
        _("Could not read the file containing the public key"), headline: :error
2573
      )
2574
    end
2575

2576
    # Removes the selected key from the list
2577
    #
2578
    # @param user [Hash] User to update
2579
    def remove_authorized_key(user)
1✔
2580
      selected_row = UI.QueryWidget(Id(:authorized_keys_table), :CurrentItem)
1✔
2581
      user["authorized_keys"].delete_at(selected_row)
1✔
2582

2583
      rows_qty = UI.QueryWidget(Id(:authorized_keys_table), :Items).size - 1
1✔
2584
      next_selected_row = selected_row == rows_qty ? selected_row - 1 : selected_row
1✔
2585
      display_authorized_keys_tab(user, next_selected_row)
1✔
2586
    end
2587

2588
    # Displays the authorized keys tab
2589
    #
2590
    # @param user         [Hash] User to update
2591
    # @param selected_row [Integer] Current selected row
2592
    def display_authorized_keys_tab(user, selected_row = nil)
1✔
2593
      UI.ReplaceWidget(:tabContents, get_authorized_keys_term(user))
5✔
2594
      UI.SetFocus(Id(:authorized_keys_table))
5✔
2595
      UI.ChangeWidget(Id(:authorized_keys_table), :CurrentItem, selected_row) if selected_row
5✔
2596
      key_present = !user.fetch("authorized_keys", []).empty?
5✔
2597
      UI.ChangeWidget(Id(:remove_authorized_key), :Enabled, key_present)
5✔
2598
    end
2599

2600

2601
    # Generates content for the authorized keys tab
2602
    #
2603
    # @param user [Hash] User to get the list of authorized keys
2604
    def get_authorized_keys_term(user)
1✔
2605
      items = user.fetch("authorized_keys", []).each_with_index.map do |content, idx|
5✔
2606
        key = Y2Users::SSHPublicKey.new(content)
4✔
2607
        Item(Id(idx), key.formatted_fingerprint, key.comment)
4✔
2608
      end
2609

2610
      VBox(
5✔
2611
        Table(
2612
          Id(:authorized_keys_table),
2613
          Opt(:notify),
2614
          Header(
2615
            # TRANSLATORS: this fingerprint is a hash that can be used to identify a public key
2616
            # (and it is usually a long string containing letters, numbers and other symbols)
2617
            _("Fingerprint"),
2618
            # TRANSLATORS: as fingerprint is hard to remember or identify for a user, a public
2619
            # key can include a comment to make things easier
2620
            _("Comment")
2621
          ),
2622
          items
2623
        ),
2624
        HBox(
2625
          # TRANSLATORS: a push button label
2626
          PushButton(Id(:add_authorized_key), _("&Add...")),
2627
          PushButton(Id(:remove_authorized_key), Yast::Label.RemoveButton),
2628
          HStretch()
2629
        )
2630
      )
2631
    end
2632
  end
2633
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc