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

yast / yast-yast2 / 13440235285

20 Feb 2025 04:40PM UTC coverage: 41.869% (-0.02%) from 41.889%
13440235285

push

github

web-flow
Merge pull request #1316 from yast/agama_kernel_conf

Respect Agama kernel parameters

2 of 4 new or added lines in 1 file covered. (50.0%)

265 existing lines in 40 files now uncovered.

12605 of 30106 relevant lines covered (41.87%)

10.76 hits per line

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

20.71
/library/packages/src/modules/SignatureCheckDialogs.rb
1
# ***************************************************************************
2
#
3
# Copyright (c) 2002 - 2012 Novell, Inc.
4
# All Rights Reserved.
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of version 2 of the GNU General Public License as
8
# published by the Free Software Foundation.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, contact Novell, Inc.
17
#
18
# To contact Novell about this file by physical or electronic mail,
19
# you may find current contact information at www.novell.com
20
#
21
# ***************************************************************************
22
# Module:    SignatureCheckDialogs.ycp
23
# Authors:    Lukas Ocilka <locilka@suse.cz>
24
#
25
# Dialogs handling for RPM/Repository GPM signatures.
26
#
27
# $Id: SignatureCheckDialogs.ycp 28363 2006-02-24 12:27:15Z locilka $
28
require "yast"
1✔
29

30
module Yast
1✔
31
  class SignatureCheckDialogsClass < Module
1✔
32
    def main
1✔
33
      Yast.import "Pkg"
1✔
34
      Yast.import "UI"
1✔
35
      textdomain "base"
1✔
36

37
      Yast.import "Label"
1✔
38
      Yast.import "Popup"
1✔
39
      Yast.import "Message"
1✔
40
      Yast.import "DontShowAgain"
1✔
41
      Yast.import "Stage"
1✔
42
      Yast.import "Linuxrc"
1✔
43

44
      # --------------------------- Don't show this dialog again Magic ---------------------------
45

46
      # /etc/sysconfig/security:CHECK_SIGNATURES
47

48
      @check_signatures = nil # lazy
1✔
49

50
      # Standard text strings
51

52
      # GnuPG fingerprint used as "Fingerprint: AAA BBB CCC"
53
      @s_fingerprint = _("Fingerprint")
1✔
54
      # GnuPG key ID used as "Key ID: 1144AAAA444"
55
      @s_keyid = _("Key ID")
1✔
56

57
      # UI can show images
58
      @has_local_image_support = nil
1✔
59

60
      # List of trusted keys
61
      #
62
      # @see bugzilla #282254
63
      @list_of_trusted_keys = []
1✔
64
    end
65

66
    # --------------------------- Don't show this dialog again Magic ---------------------------
67

68
    # Functions sets whether user want's to show the dialog again
69
    #
70
    # @param [String] popup_type dialog type
71
    # @param [true, false] show_it show again
72
    # @param [String] popup_url
73
    def SetShowThisPopup(popup_type, show_it, popup_url)
1✔
74
      if popup_type.nil? || show_it.nil?
×
75
        Builtins.y2error(
×
76
          "Neither popup_type %1 nor show_it %2 can be nil!",
77
          popup_type,
78
          show_it
79
        )
80
        return
×
81
      end
82

83
      # it's the default
84
      if show_it
×
85
        Builtins.y2debug(
×
86
          "User decision to show dialog '%1' again is '%2'",
87
          popup_type,
88
          show_it
89
        )
90
        # store only "don't show"
91
      else
92
        Builtins.y2milestone(
×
93
          "User decision to show dialog '%1' for '%2' again is '%3'",
94
          popup_type,
95
          popup_url,
96
          show_it
97
        )
98
        # Show again -> false, so, store it
99
        DontShowAgain.SetShowQuestionAgain(
×
100
          {
101
            "q_type"  => "inst-source",
102
            "q_ident" => popup_type,
103
            "q_url"   => popup_url
104
          },
105
          show_it
106
        )
107
      end
108

UNCOV
109
      nil
×
110
    end
111

112
    # Function returns whether user want's to show the dialog (again).
113
    # true is the default if nothing is set.
114
    #
115
    # @param [String] popup_type dialog type
116
    # @param [String] popup_url if any
117
    # @return [Boolean] show the dialog
118
    def GetShowThisPopup(popup_type, popup_url)
1✔
119
      if popup_type.nil?
×
120
        Builtins.y2error("popup_type %1 mustn't be nil!", popup_type)
×
121
        return true
×
122
      end
123

124
      # Read the current configuration from system configuration
125
      stored = DontShowAgain.GetShowQuestionAgain(
×
126
        "q_type"  => "inst-source",
127
        "q_ident" => popup_type,
128
        "q_url"   => popup_url
129
      )
130

131
      # Stored in the configuration?
132
      stored.nil? ? true : stored
×
133
    end
134

135
    # Function sets the default dialog return value
136
    # for case when user selected "don't show again"
137
    #
138
    # @param [String] popup_type dialog type
139
    # @param [Boolean] default_return
140
    def SetDefaultDialogReturn(popup_type, default_return, popup_url)
1✔
141
      if popup_type.nil? || default_return.nil?
×
142
        Builtins.y2error(
×
143
          "Neither popup_type %1 nor default_return %2 can be nil!",
144
          popup_type,
145
          default_return
146
        )
147
        return
×
148
      end
149
      Builtins.y2milestone(
×
150
        "User decision in default return for '%1' for '%2' is '%3'",
151
        popup_type,
152
        popup_url,
153
        default_return
154
      )
155
      DontShowAgain.SetDefaultReturn(
×
156
        {
157
          "q_type"  => "inst-source",
158
          "q_ident" => popup_type,
159
          "q_url"   => popup_url
160
        },
161
        default_return
162
      )
163

UNCOV
164
      nil
×
165
    end
166

167
    # Function returns the default popup return value
168
    # for case when user selected "don't show again"
169
    #
170
    # @param [String] popup_type dialog type
171
    # @param [String] popup_url dialog type
172
    # @return [true, false] default dialog return
173
    def GetDefaultDialogReturn(popup_type, popup_url)
1✔
174
      if popup_type.nil?
×
175
        Builtins.y2error("popup_type %1 mustn't be nil!", popup_type)
×
176
        return false
×
177
      end
178

179
      stored_return = Convert.to_boolean(
×
180
        DontShowAgain.GetDefaultReturn(
181
          "q_type"  => "inst-source",
182
          "q_ident" => popup_type,
183
          "q_url"   => popup_url
184
        )
185
      )
186

187
      Builtins.y2milestone(
×
188
        "User decided not to show popup for '%1' again, returning user-decision '%2'",
189
        popup_type,
190
        stored_return
191
      )
192
      stored_return
×
193
    end
194

195
    # A semi-public helper. Convert the kernel parameter
196
    # to the sysconfig string
197
    # @return sysconfig value: yes, yast, no
198
    def CheckSignatures
1✔
199
      cmdline = Linuxrc.InstallInf("Cmdline")
×
200
      Builtins.y2milestone("Cmdline: %1", cmdline)
×
201

202
      val = Builtins.regexpsub(
×
203
        cmdline,
204
        "CHECK_SIGNATURES=([[:alpha:]]+)",
205
        "\\1"
206
      )
207
      if val.nil?
×
208
        val = Builtins.regexpsub(cmdline, "no_sig_check=([^[:digit:]]+)", "\\1")
×
209
        if !val.nil?
×
210
          trans = { "0" => "yes", "1" => "yast", "2" => "no" }
×
211
          val = Ops.get(trans, val)
×
212
        end
213
      end
214
      val = "yes" if val.nil?
×
215
      val
×
216
    end
217

218
    # Should signatures be checked at all? Check a sysconfig variable
219
    # (or a kernel parameter for the 1st installation stage).
220
    # @return do checking?
221
    def CheckSignaturesInYaST
1✔
222
      if @check_signatures.nil?
×
223
        chs = if Stage.initial
×
224
          CheckSignatures()
×
225
        else
226
          # default is "yes"
227
          Convert.to_string(
×
228
            SCR.Read(path(".sysconfig.security.CHECK_SIGNATURES"))
229
          )
230
        end
231
        Builtins.y2milestone("CHECK_SIGNATURES: %1", chs)
×
232
        @check_signatures = chs != "no"
×
233
      end
234
      @check_signatures
×
235
    end
236

237
    # Used for unsiged file or package. Opens dialog asking whether user wants
238
    # to use this unsigned item.
239
    #
240
    # @param [Symbol] item_type `file or `package
241
    # @param [String] item_name file name or package name
242
    # @param [String] dont_show_dialog_ident for the identification in magic "don't show" functions
243
    # @return [Boolean] use or don't use ('true' if 'yes')
244
    def UseUnsignedItem(item_type, item_name, dont_show_dialog_ident, repository)
1✔
245
      Builtins.y2milestone(
×
246
        "UseUnsignedItem: type: %1, name: %2, dontshowid: %3, repo: %4",
247
        item_type,
248
        item_name,
249
        dont_show_dialog_ident,
250
        repository
251
      )
252

253
      repo = Pkg.SourceGeneralData(repository)
×
254

255
      description_text = Builtins.sformat(
×
256
        if item_type == :package
×
257
          # popup question, %1 stands for the package name
258
          # %2 is a repository name
259
          # %3 is URL of the repository
260
          _(
×
261
            "The package %1 from repository %2\n" \
262
            "%3\n" \
263
            "is not digitally signed. This means that the origin\n" \
264
            "and integrity of the package cannot be verified. Installing the package\n" \
265
            "may put the integrity of your system at risk.\n" \
266
            "\n" \
267
            "Install it anyway?"
268
          )
269
        else
270
          item_name = strip_download_prefix(item_name)
×
271
          # popup question, %1 stands for the filename
272
          # %2 is a repository name
273
          # %3 is URL of the repository
274
          _(
×
275
            "The file %1 from repository %2\n" \
276
            "%3\n" \
277
            "is not digitally signed. The origin and integrity of the file\n" \
278
            "cannot be verified. Using the file anyway puts the integrity of your \n" \
279
            "system at risk.\n" \
280
            "\n" \
281
            "Use it anyway?\n"
282
          )
283
        end,
284
        item_name,
285
        Ops.get_locale(repo, "name", _("Unknown")),
286
        Ops.get_locale(repo, "url", _("Unknown"))
287
      )
288

289
      UI.OpenDialog(
×
290
        Opt(:decorated),
291
        VBox(
292
          Heading(
293
            if item_type == :package
×
294
              _("Unsigned Package")
×
295
            else
296
              _("Unsigned File")
×
297
            end
298
          ),
299
          MarginBox(0.5, 0.5, Label(description_text)),
300
          Left(
301
            MarginBox(
302
              0,
303
              1.2,
304
              CheckBox(
305
                Id(:dont_show_again),
306
                Message.DoNotShowMessageAgain,
307
                GetShowThisPopup(dont_show_dialog_ident, item_name) ? false : true
×
308
              )
309
            )
310
          ),
311
          YesNoButtons(:no)
312
        )
313
      )
314

315
      ret = WaitForYesNoCancelUserInput()
×
316
      # default value
317
      ret = false if ret.nil?
×
318

319
      # Store the don't show value, store the default return value
320
      HandleDoNotShowDialogAgain(
×
321
        ret,
322
        dont_show_dialog_ident,
323
        :dont_show_again,
324
        item_name
325
      )
326

327
      UI.CloseDialog
×
328
      ret
×
329
    end
330

331
    # Used for file or package on signed repository but without any checksum.
332
    # Opens dialog asking whether user wants to use this item.
333
    #
334
    # @param [Symbol] item_type `file or `package
335
    # @param [String] item_name file name or package name
336
    # @param [String] dont_show_dialog_ident for the identification in magic "don't show" functions
337
    # @return [Boolean] use or don't use ('true' if 'yes')
338
    def UseItemWithNoChecksum(item_type, item_name, dont_show_dialog_ident)
1✔
339
      description_text = Builtins.sformat(
×
340
        if item_type == :package
×
341
          # popup question, %1 stands for the package name
342
          _(
×
343
            "No checksum for package %1 was found in the repository.\n" \
344
            "While the package is part of the signed repository, it is not contained \n" \
345
            "in the list of checksums in this repository. Installing the package puts \n" \
346
            "the integrity of your system at risk.\n" \
347
            "\n" \
348
            "Install it anyway?\n"
349
          )
350
        else
351
          item_name = strip_download_prefix(item_name)
×
352
          # popup question, %1 stands for the filename
353
          _(
×
354
            "No checksum for file %1 was found in the repository.\n" \
355
            "This means that the file is part of the signed repository,\n" \
356
            "but the list of checksums in this repository does not mention this file. Using the file\n" \
357
            "may put the integrity of your system at risk.\n" \
358
            "\n" \
359
            "Use it anyway?"
360
          )
361
        end,
362
        item_name
363
      )
364

365
      UI.OpenDialog(
×
366
        Opt(:decorated),
367
        VBox(
368
          # popup heading
369
          Heading(_("No Checksum Found")),
370
          MarginBox(0.5, 0.5, Label(description_text)),
371
          Left(
372
            MarginBox(
373
              0,
374
              1.2,
375
              CheckBox(
376
                Id(:dont_show_again),
377
                Message.DoNotShowMessageAgain,
378
                GetShowThisPopup(dont_show_dialog_ident, item_name) ? false : true
×
379
              )
380
            )
381
          ),
382
          YesNoButtons(:no)
383
        )
384
      )
385

386
      ret = WaitForYesNoCancelUserInput()
×
387
      # default value
388
      ret = false if ret.nil?
×
389

390
      # Store the don't show value, store the default return value
391
      HandleDoNotShowDialogAgain(
×
392
        ret,
393
        dont_show_dialog_ident,
394
        :dont_show_again,
395
        item_name
396
      )
397

398
      UI.CloseDialog
×
399
      ret
×
400
    end
401

402
    # Used for corrupted file or package. Opens dialog asking whether user wants
403
    # to use this corrupted item.
404
    #
405
    # @param [Symbol] item_type `file or `package
406
    # @param [String] item_name file name or package name
407
    # @param [Hash{String => Object}] key Used key
408
    # @return [Boolean] use or don't use ('true' if 'yes')
409
    def UseCorruptedItem(item_type, item_name, key, repository)
1✔
410
      key = deep_copy(key)
×
411
      repo = Pkg.SourceGeneralData(repository)
×
412

413
      description_text = Builtins.sformat(
×
414
        if item_type == :package
×
415
          # popup question, %1 stands for the package name, %2 for the complete description of the GnuPG key (multiline)
416
          _(
×
417
            "Package %1 from repository %2\n" \
418
            "%3\n" \
419
            "is signed with the following GnuPG key, but the integrity check failed: %4\n" \
420
            "\n" \
421
            "The package has been changed, either by accident or by an attacker,\n" \
422
            "since the repository creator signed it. Installing it is a big risk\n" \
423
            "for the integrity and security of your system.\n" \
424
            "\n" \
425
            "Install it anyway?\n"
426
          )
427
        else
428
          item_name = strip_download_prefix(item_name)
×
429
          # popup question, %1 stands for the filename, %2 for the complete description of the GnuPG key (multiline)
430
          _(
×
431
            "File %1 from repository %2\n" \
432
            "%3\n" \
433
            "is signed with the following GnuPG key, but the integrity check failed: %4\n" \
434
            "\n" \
435
            "The file has been changed, either by accident or by an attacker,\n" \
436
            "since the repository creator signed it. Using it is a big risk\n" \
437
            "for the integrity and security of your system.\n" \
438
            "\n" \
439
            "Use it anyway?\n"
440
          )
441
        end,
442
        item_name,
443
        Ops.get_locale(repo, "name", _("Unknown")),
444
        Ops.get_locale(repo, "url", _("Unknown")),
445
        Ops.add("\n\n", GPGKeyAsString(key))
446
      )
447

448
      UI.OpenDialog(
×
449
        Opt(:decorated),
450
        VBox(
451
          # popup heading
452
          Heading(_("Validation Check Failed")),
453
          MarginBox(0.5, 0.5, Label(description_text)),
454
          YesNoButtons(:no)
455
        )
456
      )
457

458
      ret = WaitForYesNoCancelUserInput()
×
459
      # default value
460
      ret = false if ret.nil?
×
461

462
      UI.CloseDialog
×
463
      ret
×
464
    end
465

466
    # Used for file or package signed by unknown key.
467
    #
468
    # @param [Symbol] item_type `file or `package
469
    # @param [String] item_name file name or package name
470
    # @param [String] key_id
471
    # @param [String] dont_show_dialog_ident for the identification in magic "don't show" functions
472
    # @param [Fixnum] repoid Id of the repository from the item was downloaded
473
    # @return [Boolean] true if 'yes, use file'
474
    def ItemSignedWithUnknownSignature(item_type, item_name, key_id, dont_show_dialog_ident, repoid)
1✔
475
      repo_url = Ops.get_string(Pkg.SourceGeneralData(repoid), "url", "")
×
476
      description_text = Builtins.sformat(
×
477
        if item_type == :package
×
478
          # popup question, %1 stands for the package name, %2 for the complex multiline description of the GnuPG key
479
          _(
×
480
            "The package %1 is digitally signed\n" \
481
            "with the following unknown GnuPG key: %2.\n" \
482
            "\n" \
483
            "This means that a trust relationship to the creator of the package\n" \
484
            "cannot be established. Installing the package may put the integrity\n" \
485
            "of your system at risk.\n" \
486
            "\n" \
487
            "Install it anyway?"
488
          )
489
        else
490
          item_name = strip_download_prefix(item_name)
×
491
          # popup question, %1 stands for the filename, %2 for the complex multiline description of the GnuPG key
492
          _(
×
493
            "The file %1\n" \
494
            "is digitally signed with the following unknown GnuPG key: %2.\n" \
495
            "\n" \
496
            "This means that a trust relationship to the creator of the file\n" \
497
            "cannot be established. Using the file may put the integrity\n" \
498
            "of your system at risk.\n" \
499
            "\n" \
500
            "Use it anyway?"
501
          )
502
        end,
503
        # TODO: use something like "%1 from %2" and make it translatable
504
        if repo_url == ""
×
505
          item_name
×
506
        else
507
          Builtins.sformat("%1 (%2)", item_name, repo_url)
×
508
        end,
509
        Ops.add(
510
          "\n",
511
          # Part of the GnuPG key description in popup, %1 is a GnuPG key ID
512
          Builtins.sformat(_("ID: %1"), key_id)
513
        )
514
      )
515

516
      UI.OpenDialog(
×
517
        Opt(:decorated),
518
        VBox(
519
          # popup heading
520
          Heading(_("Unknown GnuPG Key")),
521
          MarginBox(0.5, 0.5, Label(description_text)),
522
          Left(
523
            MarginBox(
524
              0,
525
              1.2,
526
              CheckBox(
527
                Id(:dont_show_again),
528
                Message.DoNotShowMessageAgain,
529
                GetShowThisPopup(dont_show_dialog_ident, item_name) ? false : true
×
530
              )
531
            )
532
          ),
533
          YesNoButtons(:no)
534
        )
535
      )
536

537
      # This will optionally offer to retrieve the key from gpg keyservers
538
      # That's why it will return 'symbol' instead of 'boolean'
539
      # But by now it only handles yes/no/cancel
540
      ret = WaitForYesNoCancelUserInput()
×
541
      # default value
542
      ret = false if ret.nil?
×
543

544
      # Store the don't show value, store the default return value
545
      HandleDoNotShowDialogAgain(
×
546
        ret,
547
        dont_show_dialog_ident,
548
        :dont_show_again,
549
        item_name
550
      )
551

552
      UI.CloseDialog
×
553
      ret
×
554
    end
555

556
    # Used for file or package signed by a public key. This key is still
557
    # not listed in trusted keys.
558
    #
559
    # @param item_type [Symbol] `:file` or `:package`
560
    # @param item_name [String] file name or package name
561
    # @param key [Hash<String, Object>]
562
    # @return [Symbol] `:key_import`, `:install`, `:skip`
563
    def ItemSignedWithPublicSignature(item_type, item_name, key)
1✔
564
      key = deep_copy(key)
×
565
      description_text = Builtins.sformat(
×
566
        if item_type == :package
×
567
          # popup question, %1 stands for the package name, %2 for the key ID, %3 for the key name
568
          _(
×
569
            "The package %1 is digitally signed\n" \
570
            "with key '%2 (%3)'.\n" \
571
            "\n" \
572
            "There is no trust relationship with the owner of the key.\n" \
573
            "If you trust the owner, mark the key as trusted.\n" \
574
            "\n" \
575
            "Installing a package from an unknown repository puts\n" \
576
            "the integrity of your system at risk. It is safest\n" \
577
            "to skip the package.\n"
578
          )
579
        else
580
          item_name = strip_download_prefix(item_name)
×
581
          # popup question, %1 stands for the filename, %2 for the key ID, %3 for the key name
582
          _(
×
583
            "The file %1 is digitally signed\n" \
584
            "with key '%2 (%3)'.\n" \
585
            "\n" \
586
            "There is no trust relationship with the owner of the key.\n" \
587
            "If you trust the owner, mark the key as trusted.\n" \
588
            "\n" \
589
            "Installing a file from an unknown repository puts\n" \
590
            "the integrity of your system at risk. It is safest\n" \
591
            "to skip it.\n"
592
          )
593
        end,
594
        item_name,
595
        Ops.get_string(key, "id", ""),
596
        Ops.get_string(key, "name", "")
597
      )
598

599
      UI.OpenDialog(
×
600
        Opt(:decorated),
601
        VBox(
602
          # popup heading
603
          Heading(_("Signed with Untrusted Public Key")),
604
          MarginBox(0.5, 0.5, Label(description_text)),
605
          ButtonBox(
606
            # push button
607
            PushButton(
608
              Id(:trust),
609
              Opt(:okButton, :key_F10),
610
              _("&Trust and Import the Key")
611
            ),
612
            PushButton(Id(:skip), Opt(:cancelButton, :key_F9), Label.SkipButton)
613
          )
614
        )
615
      )
616
      UI.SetFocus(:skip)
×
617

618
      # wait for one of listed ID's, return the default value in case of `cancel
619
      ret = WaitForSymbolUserInput([:trust, :skip], :skip)
×
620

621
      if ret == :trust
×
622
        # later, if asking whether to import the key, the key is trusted
623
        # so it will be also imported
624
        # bugzilla #282254
625
        @list_of_trusted_keys = Builtins.add(
×
626
          @list_of_trusted_keys,
627
          Ops.get_string(key, "id", "")
628
        )
629
      end
630

631
      UI.CloseDialog
×
632
      ret
×
633
    end
634

635
    # ImportUntrustedGPGKeyIntoTrustedDialog
636
    #
637
    # @param key [Hash<String, Object>]
638
    # @param repository [Integer]
639
    # @return [Boolean] whether zypp should import the key into the keyring of trusted keys
640
    def ImportGPGKeyIntoTrustedDialog(key, repository)
1✔
641
      key = deep_copy(key)
×
642
      # additional Richtext (HTML) warning text (kind of help), 1/2
643
      warning_text = _(
×
644
        "<p>The owner of the key may distribute updates,\n" \
645
        "packages, and package repositories that your system will trust and offer\n" \
646
        "for installation and update without any further warning. In this way,\n" \
647
        "importing the key into your keyring of trusted keys allows the key owner\n" \
648
        "to have a certain amount of control over the software on your system.</p>"
649
      ) +
650
        # additional Richtext (HTML) warning text (kind of help), 2/2
651
        _(
652
          "<p>A warning dialog opens for every package that\n" \
653
          "is not signed by a trusted (imported) key. If you do not trust the key,\n" \
654
          "the packages or repositories created by the owner of the key will not be used.</p>"
655
        )
656

657
      repo = Pkg.SourceGeneralData(repository)
×
658

659
      # popup message - label, part 1, %1 stands for repository name, %2 for its URL
660
      dialog_text = Builtins.sformat(
×
661
        _(
662
          "The following GnuPG key has been found in repository\n" \
663
          "%1\n" \
664
          "(%2):"
665
        ),
666
        Ops.get_locale(repo, "name", _("Unknown")),
667
        (repo && repo["url"]) ? repo["url"].scan(/.{1,59}/).join("\n") : _("Unknown")
×
668
      )
669

670
      # popup message - label, part 2
671
      dialog_text2 = _(
×
672
        "You can choose to import it into your keyring of trusted\n" \
673
        "public keys, meaning that you trust the owner of the key.\n" \
674
        "You should be sure that you can trust the owner and that\n" \
675
        "the key really belongs to that owner before importing it."
676
      )
677

678
      expires = Ops.get_integer(key, "expires_raw", 0)
×
679
      if Ops.greater_than(expires, 0) &&
×
680
          Ops.greater_than(Builtins.time, expires)
681
        # warning label - the key to import is expired
682
        dialog_text2 = Ops.add(
×
683
          Ops.add(Builtins.sformat(_("WARNING: The key has expired!")), "\n\n"),
684
          dialog_text2
685
        )
686
      end
687

688
      displayinfo = UI.GetDisplayInfo
×
689
      # hide additional help text in not enough wide terminals so
690
      # the important GPG key properties are completely displayed
691
      hide_help = Ops.get_boolean(displayinfo, "TextMode", false) &&
×
692
        Ops.less_than(Ops.get_integer(displayinfo, "Width", 80), 105)
693

694
      UI.OpenDialog(
×
695
        Opt(:decorated),
696
        HBox(
697
          # left-side help
698
          if hide_help
×
699
            Empty()
×
700
          else
701
            HWeight(3, VBox(RichText(warning_text)))
×
702
          end,
703
          HSpacing(1.5),
704
          # dialog
705
          HWeight(
706
            5,
707
            VBox(
708
              # popup heading
709
              Heading(_("Import Untrusted GnuPG Key")),
710
              # dialog message
711
              MarginBox(
712
                0.4,
713
                0.4,
714
                VBox(
715
                  Left(Label(dialog_text)),
716
                  GPGKeyAsTerm(key),
717
                  Left(Label(dialog_text2))
718
                )
719
              ),
720
              # dialog buttons
721
              ButtonBox(
722
                # push button
723
                PushButton(Id(:trust), Opt(:key_F10, :okButton), _("&Trust")),
724
                PushButton(
725
                  Id(:cancel),
726
                  Opt(:key_F9, :cancelButton),
727
                  Label.CancelButton
728
                )
729
              )
730
            )
731
          )
732
        )
733
      )
734

735
      UI.SetFocus(:cancel)
×
736

737
      ret = Convert.to_symbol(UI.UserInput)
×
738

739
      UI.CloseDialog
×
740

741
      ret == :trust
×
742
    end
743

744
    # Ask user to accept wrong digest
745
    # @param [String] filename Name of the file
746
    # @param [String] requested_digest Expected checksum
747
    # @param [String] found_digest Current checksum
748
    # @param [String] dont_show_dialog_ident Uniq ID for "don't show again"
749
    # @return [Boolean] true when user accepts the file
750
    def UseFileWithWrongDigest(filename, requested_digest, found_digest, dont_show_dialog_ident)
1✔
751
      filename = strip_download_prefix(filename)
×
752
      description_text =
753
        # popup question, %1 stands for the filename, %2 is expected checksum
754
        # %3 is the current checksum (e.g. "803a8ff00d00c9075a1bd223a480bcf92d2481c1")
755
        Builtins.sformat(
×
756
          _(
757
            "The expected checksum of file %1\n" \
758
            "is %2,\n" \
759
            "but the current checksum is %3.\n" \
760
            "\n" \
761
            "The file has been changed by accident or by an attacker\n" \
762
            "since the repository creator signed it. Using it is a big risk\n" \
763
            "for the integrity and security of your system.\n" \
764
            "\n" \
765
            "Use it anyway?\n"
766
          ),
767
          filename,
768
          requested_digest,
769
          found_digest
770
        )
771

772
      # dialog heading - displayed in a big bold font
773
      heading = _("Wrong Digest")
×
774

775
      RunSimpleErrorPopup(
×
776
        heading,
777
        description_text,
778
        dont_show_dialog_ident,
779
        filename
780
      )
781
    end
782

783
    # Ask user to accept a file with unknown checksum
784
    # @param [String] filename Name of the file
785
    # @param [String] digest Current checksum
786
    # @param [String] dont_show_dialog_ident Uniq ID for "don't show again"
787
    # @return [Boolean] true when user accepts the file
788
    def UseFileWithUnknownDigest(filename, digest, dont_show_dialog_ident)
1✔
789
      filename = strip_download_prefix(filename)
×
790
      description_text =
791
        # popup question, %1 stands for the filename, %2 is expected digest, %3 is the current digest
792
        Builtins.sformat(
×
793
          _(
794
            "The checksum of file %1\n" \
795
            "is %2,\n" \
796
            "but the expected checksum is not known.\n" \
797
            "\n" \
798
            "This means that the origin and integrity of the file\n" \
799
            "cannot be verified. Using the file puts the integrity of your system at risk.\n" \
800
            "\n" \
801
            "Use it anyway?\n"
802
          ),
803
          filename,
804
          digest
805
        )
806
      # dialog heading - displayed in a big bold font
807
      heading = _("Unknown Digest")
×
808

809
      RunSimpleErrorPopup(
×
810
        heading,
811
        description_text,
812
        dont_show_dialog_ident,
813
        filename
814
      )
815
    end
816

817
    publish function: :SetShowThisPopup, type: "void (string, boolean, string)"
1✔
818
    publish function: :GetShowThisPopup, type: "boolean (string, string)"
1✔
819
    publish function: :SetDefaultDialogReturn, type: "void (string, boolean, string)"
1✔
820
    publish function: :GetDefaultDialogReturn, type: "boolean (string, string)"
1✔
821
    publish function: :CheckSignatures, type: "string ()"
1✔
822
    publish function: :CheckSignaturesInYaST, type: "boolean ()"
1✔
823
    publish function: :UseUnsignedItem, type: "boolean (symbol, string, string, integer)"
1✔
824
    publish function: :UseItemWithNoChecksum, type: "boolean (symbol, string, string)"
1✔
825
    publish function: :UseCorruptedItem, type: "boolean (symbol, string, map <string, any>, integer)"
1✔
826
    publish function: :ItemSignedWithUnknownSignature, type: "boolean (symbol, string, string, string, integer)"
1✔
827
    publish function: :ItemSignedWithPublicSignature, type: "symbol (symbol, string, map <string, any>)"
1✔
828
    publish function: :ImportGPGKeyIntoTrustedDialog, type: "boolean (map <string, any>, integer)"
1✔
829
    publish function: :UseFileWithWrongDigest, type: "boolean (string, string, string, string)"
1✔
830
    publish function: :UseFileWithUnknownDigest, type: "boolean (string, string, string)"
1✔
831

832
  private
1✔
833

834
    # helper to strip download path. It uses internal knowledge that download
835
    # prefix ends in TmpDir.* zypp location
836
    def strip_download_prefix(path)
1✔
837
      path.sub(/\A\/.*\/TmpDir\.[^\/]+\//, "")
×
838
    end
839

840
    def HandleDoNotShowDialogAgain(default_return, dont_show_dialog_ident, dont_show_dialog_checkboxid, dont_show_url)
1✔
841
      dont_show_status = Convert.to_boolean(
×
842
        UI.QueryWidget(Id(dont_show_dialog_checkboxid), :Value)
843
      )
844
      # Widget doesn't exist
845
      if dont_show_status.nil?
×
846
        Builtins.y2warning(
×
847
          "No such UI widget with ID: %1",
848
          dont_show_dialog_checkboxid
849
        )
850
        # Checkbox selected -> Don't show again
851
      elsif dont_show_status == true
×
852
        Builtins.y2debug(
×
853
          "User decision -- don't show the dialog %1 again, setting default return %2",
854
          dont_show_dialog_ident,
855
          default_return
856
        )
857
        SetShowThisPopup(dont_show_dialog_ident, false, dont_show_url)
×
858
        SetDefaultDialogReturn(
×
859
          dont_show_dialog_ident,
860
          default_return,
861
          dont_show_url
862
        )
863
        # Checkbox not selected -> Show again
864
      else
865
        SetShowThisPopup(dont_show_dialog_ident, true, dont_show_url)
×
866
      end
867

UNCOV
868
      nil
×
869
    end
870

871
    # Function adds delimiter between after_chars characters in the string
872
    #
873
    # @param whattosplit [String] text to be splitted
874
    # @param delimiter [String] to place
875
    # @param after_chars [Integer] after characters
876
    # @return [String] after_chars with delimiters
877
    def StringSplitter(whattosplit, delimiter, after_chars)
1✔
878
      splittedstring = ""
×
879
      after_chars_counter = 0
×
880
      max_size = Builtins.size(whattosplit)
×
881

882
      loop do
×
883
        if Ops.greater_or_equal(
×
884
          Ops.add(after_chars_counter, after_chars),
885
          max_size
886
        )
887
          splittedstring = Ops.add(
×
888
            Ops.add(splittedstring, (splittedstring == "") ? "" : delimiter),
×
889
            Builtins.substring(whattosplit, after_chars_counter)
890
          )
891
          break
×
892
        else
893
          splittedstring = Ops.add(
×
894
            Ops.add(splittedstring, (splittedstring == "") ? "" : delimiter),
×
895
            Builtins.substring(whattosplit, after_chars_counter, after_chars)
896
          )
897
          after_chars_counter = Ops.add(after_chars_counter, after_chars)
×
898
        end
899
      end
900

901
      splittedstring
×
902
    end
903

904
    # Returns term of yes/no buttons
905
    #
906
    # @param default_button [Symbol] `:yes` or `:no`. If another value is passed, `:no` is used.
907
    # @return [Yast::Term] with buttons
908
    def YesNoButtons(default_button)
1✔
909
      yes_button = PushButton(
×
910
        Id(:yes),
911
        Opt(:okButton, :key_F10),
912
        Label.YesButton
913
      )
914
      no_button = PushButton(
×
915
        Id(:no),
916
        Opt(:cancelButton, :key_F9),
917
        Label.NoButton
918
      )
919

920
      if default_button == :yes
×
921
        yes_button = PushButton(
×
922
          Id(:yes),
923
          Opt(:default, :okButton, :key_F10),
924
          Label.YesButton
925
        )
926
      else
927
        no_button = PushButton(
×
928
          Id(:no),
929
          Opt(:default, :cancelButton, :key_F9),
930
          Label.NoButton
931
        )
932
      end
933

934
      ButtonBox(yes_button, no_button)
×
935
    end
936

937
    # Returns 'true' (yes), 'false' (no) or 'nil' (cancel)
938
    #
939
    # @return [Boolean] user input yes==true
940
    def WaitForYesNoCancelUserInput
1✔
941
      user_input = nil
×
942
      ret = nil
×
943

944
      loop do
×
945
        user_input = UI.UserInput
×
946
        # yes button
947
        case user_input
×
948
        when :yes
949
          ret = true
×
950
          break
×
951
          # no button
952
        when :no
953
          ret = false
×
954
          break
×
955
          # closing window uisng [x]
956
        when :cancel
957
          ret = nil
×
958
          break
×
959
        else
960
          Builtins.y2error("Unknown user input: '%1'", user_input)
×
961
          next
×
962
        end
963
      end
964

965
      ret
×
966
    end
967

968
    # Waits for user input and checks it agains accepted symbols.
969
    # Returns the default symbol in case of `cancel (user closes the dialog).
970
    #
971
    # @param list_of_accepted [Array<Symbol>] of accepted symbol by UserInput
972
    # @param default_symb [Symbol] default return for case of `cancel
973
    def WaitForSymbolUserInput(list_of_accepted, default_symb)
1✔
974
      list_of_accepted = deep_copy(list_of_accepted)
×
975
      user_input = nil
×
976
      ret = nil
×
977

978
      loop do
×
979
        user_input = Convert.to_symbol(UI.UserInput)
×
980
        if Builtins.contains(list_of_accepted, user_input)
×
981
          ret = user_input
×
982
          break
×
983
        elsif user_input == :cancel
×
984
          ret = default_symb
×
985
          break
×
986
        else
987
          Builtins.y2error("Unknown user input: '%1'", user_input)
×
988
          next
×
989
        end
990
      end
991

992
      ret
×
993
    end
994

995
    # FIXME: add GPG class that have method to_string and to_term
996
    def GPGKeyAsString(key)
1✔
997
      key = deep_copy(key)
×
998
      # Part of the GnuPG key description in popup, %1 is a GnuPG key ID
999
      Ops.add(
×
1000
        Ops.add(
1001
          Ops.add(
1002
            Ops.add(
1003
              Ops.add(
1004
                Builtins.sformat(_("ID: %1"), Ops.get_string(key, "id", "")),
1005
                "\n"
1006
              ),
1007
              if Ops.get_string(key, "fingerprint", "").nil? ||
×
1008
                Ops.get_string(key, "fingerprint", "") == ""
1009
                # Part of the GnuPG key description in popup, %1 is a GnuPG key fingerprint
1010
                ""
×
1011
              else
1012
                Builtins.sformat(
×
1013
                  _("Fingerprint: %1") + "\n",
1014
                  StringSplitter(Ops.get_string(key, "fingerprint", ""), " ", 4)
1015
                )
1016
              end
1017
            ),
1018
            # Part of the GnuPG key description in popup, %1 is a GnuPG key name
1019
            Builtins.sformat(_("Name: %1"), Ops.get_string(key, "name", ""))
1020
          ),
1021
          if Ops.get_string(key, "created", "") == ""
×
1022
            ""
×
1023
          else
1024
            Ops.add(
×
1025
              "\n",
1026
              Builtins.sformat(
1027
                _("Created: %1"),
1028
                Ops.get_string(key, "created", "")
1029
              )
1030
            )
1031
          end
1032
        ),
1033
        if Ops.get_string(key, "expires", "") == ""
×
1034
          ""
×
1035
        else
1036
          Ops.add(
×
1037
            "\n",
1038
            Builtins.sformat(
1039
              _("Expires: %1"),
1040
              Ops.get_string(key, "expires", "")
1041
            )
1042
          )
1043
        end
1044
      )
1045
    end
1046

1047
    # FIXME: add GPG class that have method to_string and to_term
1048
    def GPGKeyAsTerm(key)
1✔
1049
      key = deep_copy(key)
×
1050
      rt = Ops.add(
×
1051
        # GPG key property
1052
        Builtins.sformat(
1053
          "<b>%1</b>%2",
1054
          _("ID: "),
1055
          Ops.get_string(key, "id", "")
1056
        ),
1057
        # GPG key property
1058
        Builtins.sformat(
1059
          "<br><b>%1</b>%2",
1060
          _("Name: "),
1061
          Ops.get_string(key, "name", "")
1062
        )
1063
      )
1064
      if Ops.greater_than(
×
1065
        Builtins.size(Ops.get_string(key, "fingerprint", "")),
1066
        0
1067
      )
1068
        # GPG key property
1069
        rt = Ops.add(
×
1070
          rt,
1071
          Builtins.sformat(
1072
            "<br><b>%1</b>%2",
1073
            _("Fingerprint: "),
1074
            StringSplitter(Ops.get_string(key, "fingerprint", ""), " ", 4)
1075
          )
1076
        )
1077
      end
1078
      if Ops.greater_than(Builtins.size(Ops.get_string(key, "created", "")), 0)
×
1079
        # GPG key property
1080
        rt = Ops.add(
×
1081
          rt,
1082
          Builtins.sformat(
1083
            "<br><b>%1</b>%2",
1084
            _("Created: "),
1085
            Ops.get_string(key, "created", "")
1086
          )
1087
        )
1088
      end
1089
      if Ops.greater_than(Builtins.size(Ops.get_string(key, "expires", "")), 0)
×
1090
        # GPG key property
1091
        rt = Ops.add(
×
1092
          rt,
1093
          Builtins.sformat(
1094
            "<br><b>%1</b>%2",
1095
            _("Expires: "),
1096
            Ops.get_string(key, "expires", "")
1097
          )
1098
        )
1099
      end
1100
      RichText(rt)
×
1101
    end
1102

1103
    def RunSimpleErrorPopup(heading, description_text, dont_show_dialog_ident, dont_show_dialog_param)
1✔
1104
      UI.OpenDialog(
×
1105
        Opt(:decorated),
1106
        VBox(
1107
          # dialog heading - displayed in a big bold font
1108
          Heading(heading),
1109
          MarginBox(0.5, 0.5, Label(description_text)),
1110
          Left(
1111
            MarginBox(
1112
              0,
1113
              1.2,
1114
              CheckBox(
1115
                Id(:dont_show_again),
1116
                Message.DoNotShowMessageAgain,
1117
                GetShowThisPopup(dont_show_dialog_ident, dont_show_dialog_param) ? false : true
×
1118
              )
1119
            )
1120
          ),
1121
          YesNoButtons(:no)
1122
        )
1123
      )
1124

1125
      ret = WaitForYesNoCancelUserInput()
×
1126
      # default value
1127
      ret = false if ret.nil?
×
1128

1129
      # Store the don't show value, store the default return value
1130
      HandleDoNotShowDialogAgain(
×
1131
        ret,
1132
        dont_show_dialog_ident,
1133
        :dont_show_again,
1134
        dont_show_dialog_param
1135
      )
1136

1137
      UI.CloseDialog
×
1138

1139
      ret
×
1140
    end
1141
  end
1142

1143
  SignatureCheckDialogs = SignatureCheckDialogsClass.new
1✔
1144
  SignatureCheckDialogs.main
1✔
1145
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

© 2026 Coveralls, Inc