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

yast / yast-yast2 / 13439774818

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

Pull #1316

github

web-flow
Merge aa675c3b3 into 037b94b1f
Pull Request #1316: 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

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

24
# File:  modules/CWMFirewallInterfaces.ycp
25
# Package:  Common widget manipulation, firewall interfaces widget
26
# Summary:  Routines for selecting interfaces opened in firewall
27
# Authors:  Jiri Srain <jsrain@suse.cz>
28
#
29
# $Id$
30
#
31
# WARNING: If you want to use this functionality of this module
32
#          you should allways call 'firewalld.read' in the
33
#          Read() function of you module
34
#          and you should call 'firewalld.write' in the
35
#          Write() function.
36
#
37
#      Functionality of this module only changes the firewalld
38
#          settings in memory, it never Reads or Writes the settings.
39

40
require "yast"
1✔
41
require "y2firewall/firewalld"
1✔
42
require "y2firewall/helpers/interfaces"
1✔
43

44
module Yast
1✔
45
  # This class provide a set of methods to define a widget for handling with
46
  # firewall interfaces configuration.
47
  #
48
  # @example Open a service in the firewall
49
  #
50
  #   # Obtain the firewalld widget description (a widget with the firewalld
51
  #   # status, a checkbox for opening the service in all the interfaces and
52
  #   # a details button to decide per interface where should be opened.
53
  #
54
  #   CWMFireallInterfaces.CreateOpenFirewallWidget(["smtp"])
55
  #
56
  #   # Require the new firewalld library
57
  #   require 'y2firewall/firewalld'
58
  #
59
  #   # In your Read method call firewalld.read to initializate the firewalld
60
  #   # object and all of its zones.
61
  #
62
  #   Y2Firewall::Firewalld.instance.read
63
  #
64
  #   # And finally call firewalld.write to apply the changes done through the
65
  #   # widget.
66
  #
67
  #   Y2Firewall::Firewalld.instance.write
68
  class CWMFirewallInterfacesClass < Module
1✔
69
    include Y2Firewall::Helpers::Interfaces
1✔
70
    include Yast::Logger
1✔
71

72
    # [Array<String>] List of all interfaces relevant for firewall settings
73
    attr_reader :allowed_interfaces
1✔
74
    # [Array<String>] List of all the system network interfaces
75
    attr_reader :all_interfaces
1✔
76

77
    # [Boolean] Information if configuration was changed by user
78
    attr_reader :configuration_changed
1✔
79

80
    def main
1✔
81
      Yast.import "UI"
1✔
82
      textdomain "base"
1✔
83

84
      Yast.import "CWM"
1✔
85
      Yast.import "Label"
1✔
86
      Yast.import "Mode"
1✔
87
      Yast.import "NetworkInterfaces"
1✔
88
      Yast.import "Popup"
1✔
89
      Yast.import "Report"
1✔
90
      Yast.import "Stage"
1✔
91
      Yast.import "String"
1✔
92

93
      # private variables
94

95
      # List of all items of interfaces to the selection box
96
      @interface_items = nil
1✔
97

98
      @configuration_changed = false
1✔
99

100
      @buggy_ifaces = []
1✔
101
    end
102

103
    # private functions
104

105
    # Enable or disable the firewall details widget according to the status
106
    # of "open firewall" checkbox
107
    def EnableOrDisableFirewallDetails
1✔
108
      return if !UI.WidgetExists(Id("_cwm_open_firewall"))
×
109
      return if !UI.WidgetExists(Id("_cwm_firewall_details"))
×
110

111
      enabled = Convert.to_boolean(
×
112
        UI.QueryWidget(Id("_cwm_open_firewall"), :Value)
113
      )
114
      enabled = false if enabled.nil? || all_interfaces.empty?
×
115

116
      UI.ChangeWidget(Id("_cwm_firewall_details"), :Enabled, enabled)
×
117

UNCOV
118
      nil
×
119
    end
120

121
    # Set the firewall status label
122
    # @param [Symbol] status symbol one of `off, `closed, `open_all, `custom, `not_installed
123
    def SetFirewallLabel(status)
1✔
124
      UI.ReplaceWidget(Id(:_cwm_firewall_status_rp), Label(firewall_status_label(status)))
×
125

UNCOV
126
      nil
×
127
    end
128

129
    # Initialize the list of all known interfaces
130
    def InitAllInterfacesList
1✔
131
      # Do not read NetworkInterfaces when they are already read
132
      if !Mode.config && !Mode.installation && !Mode.update
2✔
133
        log.info("Reading NetworkInterfaces...")
1✔
134
        NetworkInterfaces.Read
1✔
135
      end
136

137
      @all_interfaces = NetworkInterfaces.List("").reject { |i| i == "lo" }
2✔
138
      @interface_items = all_interfaces.map { |i| Item(Id(i), interface_label(i)) }
2✔
139

140
      nil
2✔
141
    end
142

143
    # Update the firewall status label according to the current status
144
    def UpdateFirewallStatus
1✔
145
      InitAllInterfacesList() if all_interfaces.nil?
×
146

147
      status = current_firewall_status
×
148
      log.info("Status: #{status}, All: #{all_interfaces}, Allowed: #{allowed_interfaces}")
×
149

150
      SetFirewallLabel(status)
×
151
      open = [:open_all, :custom].include?(status)
×
152
      UI.ChangeWidget(Id("_cwm_open_firewall"), :Value, open)
×
153

UNCOV
154
      nil
×
155
    end
156

157
    # Get the list of all interfaces that will be selected
158
    #
159
    # @param [Array<String>] ifaces a list of interfaces selected by the user
160
    # @param [Boolean] _nm_ifaces_have_to_be_selected defines whether also NetworkManager have to be selected too
161
    # @return a list of interfaces that will be opened
162
    def Selected2Opened(ifaces, _nm_ifaces_have_to_be_selected)
1✔
163
      log.info("Selected ifaces: #{ifaces}")
1✔
164
      zone_names = ifaces.map do |name|
1✔
165
        zone = interface_zone(name)
1✔
166
        zone ? zone.name : default_zone.name
1✔
167
      end
168
      zone_names.uniq!
1✔
169
      log.info("Ifaces zone names: #{zone_names}")
1✔
170

171
      zone_ifaces =
172
        zone_names.map do |zone_name|
1✔
173
          zone = firewalld.find_zone(zone_name)
1✔
174
          next [] unless zone
1✔
175

176
          interfaces = zone.interfaces
1✔
177

178
          next(interfaces) unless zone_name == default_zone.name
1✔
179

180
          interfaces += default_interfaces
1✔
181

182
          left_explicitly = interfaces.select { |i| ifaces.include?(i) }.uniq
3✔
183
          log.info("Ifaces left in zone: #{left_explicitly}")
1✔
184

185
          next [] if left_explicitly.empty?
1✔
186

187
          interfaces
1✔
188
        end
189

190
      zone_ifaces.flatten.uniq
1✔
191
    end
192

193
    # Display popup with firewall settings details
194
    def DisplayFirewallDetailsPopupHandler(widget)
1✔
195
      widget = deep_copy(widget)
×
196
      common_details_handler = Convert.convert(
×
197
        Ops.get(widget, "common_details_handler"),
198
        from: "any",
199
        to:   "void (map <string, any>)"
200
      )
201
      common_details_handler&.call(widget)
×
202

UNCOV
203
      nil
×
204
    end
205

206
    # public functions
207

208
    # general functions
209

210
    # Initialize the list of allowed interfaces
211
    # Changes the internal variables
212
    # @param [Array<String>] services a list of services
213
    def InitAllowedInterfaces(services)
1✔
214
      service_status = {}
×
215

216
      zone_services(services).each do |_s, status|
×
217
        status.each do |iface, en|
×
218
          service_status[iface] = service_status.fetch(iface, true) && en
×
219
        end
220
      end
221

222
      service_status.select! { |_iface, en| en == true }
×
223
      log.info("Status: #{service_status}")
×
224
      @allowed_interfaces = service_status.keys
×
225

226
      log.info "Default zone name: #{default_zone.name}"
×
227
      log.info "Default interfaces: #{default_interfaces}"
×
228
      log.info "Default_zone services: #{default_zone.services}"
×
229

230
      if !default_interfaces.empty?
×
231
        services.each do |service|
×
232
          service_status[firewalld.default_zone] =
×
233
            default_zone&.services&.include?(service)
234
        end
235
        @allowed_interfaces = (allowed_interfaces + default_interfaces).uniq if service_status[firewalld.default_zone]
×
236
      end
237

238
      log.info "Allowed interfaces: #{allowed_interfaces}"
×
239

240
      @configuration_changed = false
×
241

UNCOV
242
      nil
×
243
    end
244

245
    # Store the list of allowed interfaces
246
    # Users the internal variables
247
    # @param [Array<String>] services a list of services
248
    def StoreAllowedInterfaces(services)
1✔
249
      services = deep_copy(services)
1✔
250
      # do not save anything if configuration didn't change
251
      return if !configuration_changed
1✔
252

253
      zones =
254
        known_interfaces.each_with_object([]) do |iface, memo|
1✔
255
          if allowed_interfaces.include?(iface.name)
3✔
256
            zone_name = iface.zone ? iface.zone.name : firewalld.default_zone
3✔
257
            memo << zone_name
3✔
258
          end
259
        end
260

261
      firewalld.zones.each do |zone|
1✔
262
        if zones.include?(zone.name)
2✔
263
          services.each do |service|
2✔
264
            zone.add_service(service) unless zone.services.include?(service)
2✔
265
          end
266
        else
267
          services.each do |service|
×
268
            zone.remove_service(service) if zone.services.include?(service)
×
269
          end
270
        end
271
      end
272

273
      nil
1✔
274
    end
275

276
    # Init function of the widget
277
    # @param [Hash<String, Object>] _widget a widget description map
278
    # @param [String] _key strnig the widget key
279
    def InterfacesInit(_widget, _key)
1✔
280
      # set the list of ifaces
281
      InitAllInterfacesList() if all_interfaces.nil?
×
282
      UI.ReplaceWidget(
×
283
        Id("_cwm_interface_list_rp"),
284
        MultiSelectionBox(
285
          Id("_cwm_interface_list"),
286
          # transaltors: selection box title
287
          _("&Network Interfaces with Open Port in Firewall"),
288
          @interface_items
289
        )
290
      )
291
      # mark open ifaces as open
292
      UI.ChangeWidget(
×
293
        Id("_cwm_interface_list"),
294
        :SelectedItems,
295
        allowed_interfaces
296
      )
297

UNCOV
298
      nil
×
299
    end
300

301
    # Handle function of the widget
302
    # @param [Hash<String, Object>] _widget a widget description map
303
    # @param [String] _key strnig the widget key
304
    # @param [Hash] event map event to be handled
305
    # @return [Symbol] for wizard sequencer or nil
306
    def InterfacesHandle(_widget, _key, event)
1✔
307
      event_id = Ops.get(event, "ID")
×
308
      if event_id == "_cwm_interface_select_all"
×
309
        UI.ChangeWidget(
×
310
          Id("_cwm_interface_list"),
311
          :SelectedItems,
312
          all_interfaces
313
        )
314
        return nil
×
315
      end
316
      if event_id == "_cwm_interface_select_none"
×
317
        UI.ChangeWidget(Id("_cwm_interface_list"), :SelectedItems, [])
×
318
        return nil
×
319
      end
UNCOV
320
      nil
×
321
    end
322

323
    # Store function of the widget
324
    # @param [Hash<String, Object>] _widget a widget description map
325
    # @param [String] _key strnig the widget key
326
    # @param [Hash] _event map that caused widget data storing
327
    def InterfacesStore(_widget, _key, _event)
1✔
328
      allowed_interfaces = Convert.convert(
×
329
        UI.QueryWidget(Id("_cwm_interface_list"), :SelectedItems),
330
        from: "any",
331
        to:   "list <string>"
332
      )
333
      @allowed_interfaces = Selected2Opened(allowed_interfaces, false)
×
334
      @configuration_changed = true
×
335

UNCOV
336
      nil
×
337
    end
338

339
    # Validate function of the widget
340
    # @param [Hash<String, Object>] _widget a widget description map
341
    # @param [String] _key strnig the widget key
342
    # @param [Hash] _event map event that caused the validation
343
    # @return true if validation succeeded, false otherwise
344
    def InterfacesValidate(_widget, _key, _event)
1✔
345
      trusted_zone = firewalld.find_zone("trusted")
×
346

347
      ifaces = Convert.convert(
×
348
        UI.QueryWidget(Id("_cwm_interface_list"), :SelectedItems),
349
        from: "any",
350
        to:   "list <string>"
351
      ) || []
352

353
      log.info("Selected ifaces: #{ifaces}")
×
354

355
      trusted_interfaces = trusted_zone ? trusted_zone.interfaces : []
×
356

357
      if !trusted_interfaces.empty?
×
358
        int_not_selected = []
×
359
        trusted_interfaces.each do |interface|
×
360
          int_not_selected << interface unless ifaces.include?(interface)
×
361
        end
362

363
        if !int_not_selected.empty?
×
364
          log.warn("Unprotected internal interfaces not selected: #{int_not_selected}")
×
365

366
          Report.Message(
×
367
            Builtins.sformat(
368
              _(
369
                "These network interfaces assigned to internal network cannot be deselected:\n%1\n"
370
              ),
371
              int_not_selected.join("\n")
372
            )
373
          )
374

375
          ifaces = Convert.convert(
×
376
            Builtins.union(ifaces, int_not_selected),
377
            from: "list",
378
            to:   "list <string>"
379
          )
380
          log.info("Selected interfaces: #{ifaces}")
×
381
          UI.ChangeWidget(Id("_cwm_interface_list"), :SelectedItems, ifaces)
×
382
          return false
×
383
        end
384
      end
385

386
      if ifaces.empty? && !Popup.YesNo(
×
387
          _(
388
            "No interface is selected. Service will not\n" \
389
            "be available for other computers.\n" \
390
            "\n" \
391
            "Continue?"
392
          )
393
        )
394
        return false
×
395
      end
396

397
      firewall_ifaces = Selected2Opened(ifaces, false)
×
398
      log.info("firewall_ifaces: #{firewall_ifaces}")
×
399
      added_ifaces = firewall_ifaces.reject { |i| ifaces.include?(i) }
×
400
      log.info("added_ifaces: #{added_ifaces}")
×
401
      removed_ifaces = ifaces.reject { |i| firewall_ifaces.include?(i) }
×
402
      log.info("removed_ifaces: #{removed_ifaces}")
×
403

404
      # to hide that special string
405
      if !added_ifaces.empty? && !Popup.YesNo(
×
406
          Builtins.sformat(
407
            # yes-no popup
408
            _(
409
              "Because of firewalld settings, the port\n" \
410
              "on the following interfaces will additionally be open:\n" \
411
              "%1\n" \
412
              "\n" \
413
              "Continue?"
414
            ),
415
            added_ifaces.join("\n")
416
          )
417
        )
418
        return false
×
419
      end
420

421
      # to hide that special string
422
      if !removed_ifaces.empty? && !Popup.YesNo(
×
423
          Builtins.sformat(
424
            # yes-no popup
425
            _(
426
              "Because of firewalld settings, the port\n" \
427
              "on the following interfaces cannot be opened:\n" \
428
              "%1\n" \
429
              "\n" \
430
              "Continue?"
431
            ),
432
            removed_ifaces.join("\n")
433
          )
434
        )
435
        return false
×
436
      end
437

438
      true
×
439
    end
440

441
    # Init function of the widget
442
    # @param [String] key strnig the widget key
443
    def InterfacesInitWrapper(key)
1✔
444
      InterfacesInit(CWM.GetProcessedWidget, key)
×
445

UNCOV
446
      nil
×
447
    end
448

449
    # Handle function of the widget
450
    # @param [String] key strnig the widget key
451
    # @param [Hash] event map event to be handled
452
    # @return [Symbol] for wizard sequencer or nil
453
    def InterfacesHandleWrapper(key, event)
1✔
454
      event = deep_copy(event)
×
455
      InterfacesHandle(CWM.GetProcessedWidget, key, event)
×
456
    end
457

458
    # Store function of the widget
459
    # @param [String] key strnig the widget key
460
    # @param [Hash] event map that caused widget data storing
461
    def InterfacesStoreWrapper(key, event)
1✔
462
      event = deep_copy(event)
×
463
      InterfacesStore(CWM.GetProcessedWidget, key, event)
×
464

UNCOV
465
      nil
×
466
    end
467

468
    # Validate function of the widget
469
    # @param [String] key strnig the widget key
470
    # @param [Hash] event map event that caused the validation
471
    # @return true if validation succeeded, false otherwise
472
    def InterfacesValidateWrapper(key, event)
1✔
473
      event = deep_copy(event)
×
474
      InterfacesValidate(CWM.GetProcessedWidget, key, event)
×
475
    end
476

477
    # Get the widget description map
478
    # @param [Hash{String => Object}] settings a map of all parameters needed to create the widget properly
479
    # <pre>
480
    #
481
    # Behavior manipulating functions (mandatory)
482
    # - "get_allowed_interfaces" : list<string>() -- function that returns
483
    #          the list of allowed network interfaces
484
    # - "set_allowed_interfaces" : void (list<string>) -- function that sets
485
    #          the list of allowed interfaces
486
    #
487
    # Additional settings:
488
    # - "help" : string -- help to the whole widget. If not specified, generic help
489
    #          is used (button labels are patched correctly)
490
    # </pre>
491
    # @return [Hash] the widget description map
492
    def CreateInterfacesWidget(settings)
1✔
493
      settings = deep_copy(settings)
×
494
      widget = HBox(
×
495
        HSpacing(1),
496
        VBox(
497
          HSpacing(48),
498
          VSpacing(1),
499
          ReplacePoint(
500
            Id("_cwm_interface_list_rp"),
501
            MultiSelectionBox(
502
              Id("_cwm_interface_list"),
503
              # translators: selection box title
504
              _("Network &Interfaces with Open Port in Firewall"),
505
              []
506
            )
507
          ),
508
          VSpacing(1),
509
          HBox(
510
            HStretch(),
511
            HWeight(
512
              1,
513
              PushButton(
514
                Id("_cwm_interface_select_all"),
515
                # push button to select all network intefaces for firewall
516
                _("Select &All")
517
              )
518
            ),
519
            HWeight(
520
              1,
521
              PushButton(
522
                Id("_cwm_interface_select_none"),
523
                # push button to deselect all network intefaces for firewall
524
                _("Select &None")
525
              )
526
            ),
527
            HStretch()
528
          ),
529
          VSpacing(1)
530
        ),
531
        HSpacing(1)
532
      )
533

534
      help = "" # TODO
×
535

536
      help = Ops.get_string(settings, "help", "") if Builtins.haskey(settings, "help")
×
537

538
      ret = Convert.convert(
×
539
        Builtins.union(
540
          settings,
541
          "widget"            => :custom,
542
          "custom_widget"     => widget,
543
          "help"              => help,
544
          "init"              => fun_ref(
545
            method(:InterfacesInitWrapper),
546
            "void (string)"
547
          ),
548
          "store"             => fun_ref(
549
            method(:InterfacesStoreWrapper),
550
            "void (string, map)"
551
          ),
552
          "handle"            => fun_ref(
553
            method(:InterfacesHandleWrapper),
554
            "symbol (string, map)"
555
          ),
556
          "validate_type"     => :function,
557
          "validate_function" => fun_ref(
558
            method(:InterfacesValidateWrapper),
559
            "boolean (string, map)"
560
          )
561
        ),
562
        from: "map",
563
        to:   "map <string, any>"
564
      )
565

566
      deep_copy(ret)
×
567
    end
568

569
    # Display the firewall interfaces selection as a popup
570
    # @return [Symbol] return value of the dialog
571
    def DisplayDetailsPopup(settings)
1✔
572
      settings = deep_copy(settings)
×
573
      # FIXME: breaks help if run in dialog with Tab!!!!!!
574
      # settings stack must be created in CWM::Run
575
      w = CWM.CreateWidgets(
×
576
        ["firewall_ifaces"],
577
        "firewall_ifaces" => CreateInterfacesWidget(settings)
578
      )
579
      contents = VBox(
×
580
        "firewall_ifaces",
581
        ButtonBox(
582
          PushButton(Id(:ok), Opt(:okButton, :key_F10), Label.OKButton),
583
          PushButton(
584
            Id(:cancel),
585
            Opt(:cancelButton, :key_F9),
586
            Label.CancelButton
587
          )
588
        )
589
      )
590
      contents = CWM.PrepareDialog(contents, w)
×
591
      UI.OpenDialog(contents)
×
592
      ret = CWM.Run(w, {})
×
593
      UI.CloseDialog
×
594
      ret
×
595
    end
596

597
    # firewall openning widget
598

599
    # Initialize the open firewall widget
600
    # @param [Hash{String => Object}] widget a map describing the whole widget
601
    def OpenFirewallInit(widget, _key)
1✔
602
      return unless open_firewall_widget?
9✔
603

604
      services = widget.fetch("services", [])
8✔
605

606
      InitAllInterfacesList()
8✔
607
      InitAllowedInterfaces(services)
8✔
608

609
      open_firewall = !allowed_interfaces.empty?
8✔
610
      firewall_enabled = firewalld.enabled? && !all_interfaces.empty?
8✔
611
      if !firewall_enabled
8✔
612
        open_firewall = false
4✔
613
        UI.ChangeWidget(Id("_cwm_open_firewall"), :Enabled, false)
4✔
614
      end
615
      UI.ChangeWidget(Id("_cwm_open_firewall"), :Value, open_firewall)
8✔
616
      UpdateFirewallStatus()
8✔
617
      EnableOrDisableFirewallDetails()
8✔
618

619
      nil
8✔
620
    end
621

622
    # Store function of the widget
623
    # @param [Hash] widget widget description
624
    # @param [String] _key strnig the widget key
625
    # @param [Hash] _event map that caused widget data storing
626
    def OpenFirewallStore(widget, _key, _event)
1✔
627
      return unless open_firewall_widget?
×
628

629
      services = widget.fetch("services", [])
×
630
      StoreAllowedInterfaces(services)
×
UNCOV
631
      nil
×
632
    end
633

634
    # Handle the immediate start and stop of the service
635
    # @param [Hash<String, Object>] widget a map describing the widget
636
    # @param [String] _key strnig the widget key
637
    # @param [Hash<String, Object>] event event to handle
638
    # @return always nil
639
    def OpenFirewallHandle(widget, _key, event)
1✔
640
      widget = deep_copy(widget)
×
641
      event = deep_copy(event)
×
642
      event_id = Ops.get(event, "ID")
×
643
      if event_id == "_cwm_firewall_details"
×
644
        handle_firewall_details = Convert.convert(
×
645
          Ops.get(widget, "firewall_details_handler"),
646
          from: "any",
647
          to:   "symbol ()"
648
        )
649
        Builtins.y2milestone("FD: %1", handle_firewall_details)
×
650
        ret = nil
×
651
        Builtins.y2milestone("RT: %1", ret)
×
652
        if handle_firewall_details.nil?
×
653
          w = Builtins.filter(widget) { |k, _v| "services" == k }
×
654
          DisplayDetailsPopup(w)
×
655
        else
656
          ret = handle_firewall_details.call
×
657
        end
658
        UpdateFirewallStatus()
×
659
        EnableOrDisableFirewallDetails()
×
660
        return ret
×
661
      end
662
      if event_id == "_cwm_open_firewall"
×
663
        value = Convert.to_boolean(
×
664
          UI.QueryWidget(Id("_cwm_open_firewall"), :Value)
665
        )
666
        Builtins.y2milestone("OF: %1", value)
×
667
        @allowed_interfaces = value ? deep_copy(all_interfaces) : []
×
668

669
        @buggy_ifaces = []
×
670

671
        # Filtering out buggy ifaces
672
        Builtins.foreach(@buggy_ifaces) do |one_iface|
×
673
          @allowed_interfaces = Builtins.filter(allowed_interfaces) do |one_allowed|
×
674
            one_allowed != one_iface
×
675
          end
676
        end
677

678
        UpdateFirewallStatus()
×
679
        EnableOrDisableFirewallDetails()
×
680
        @configuration_changed = true
×
681
      end
UNCOV
682
      nil
×
683
    end
684

685
    # Init function of the widget
686
    # @param [String] key strnig the widget key
687
    def OpenFirewallInitWrapper(key)
1✔
688
      OpenFirewallInit(CWM.GetProcessedWidget, key)
×
689

UNCOV
690
      nil
×
691
    end
692

693
    # Store function of the widget
694
    # @param [String] key strnig the widget key
695
    # @param [Hash] event map that caused widget data storing
696
    def OpenFirewallStoreWrapper(key, event)
1✔
697
      event = deep_copy(event)
×
698
      OpenFirewallStore(CWM.GetProcessedWidget, key, event)
×
699

UNCOV
700
      nil
×
701
    end
702

703
    # Handle the immediate start and stop of the service
704
    # @param [String] key strnig the widget key
705
    # @param [Hash<String, Object>] event to handle
706
    # @return always nil
707
    def OpenFirewallHandleWrapper(key, event)
1✔
708
      event = deep_copy(event)
×
709
      OpenFirewallHandle(CWM.GetProcessedWidget, key, event)
×
710
    end
711

712
    # Check if the widget was modified
713
    # @param [String] _key the widget key
714
    # @return [Boolean] true if widget was modified
715
    def OpenFirewallModified(_key)
1✔
716
      @configuration_changed
×
717
    end
718

719
    # Enable the whole firewal widget
720
    def EnableOpenFirewallWidget
1✔
721
      return if !UI.WidgetExists(Id("_cwm_open_firewall"))
×
722
      return if !UI.WidgetExists(Id("_cwm_firewall_details"))
×
723

724
      UI.ChangeWidget(Id("_cwm_open_firewall"), :Enabled, true)
×
725
      EnableOrDisableFirewallDetails()
×
726

UNCOV
727
      nil
×
728
    end
729

730
    # Disable the whole firewal widget
731
    def DisableOpenFirewallWidget
1✔
732
      return if !UI.WidgetExists(Id("_cwm_open_firewall"))
×
733
      return if !UI.WidgetExists(Id("_cwm_firewall_details"))
×
734

735
      UI.ChangeWidget(Id("_cwm_open_firewall"), :Enabled, false)
×
736
      UI.ChangeWidget(Id("_cwm_firewall_details"), :Enabled, false)
×
737

UNCOV
738
      nil
×
739
    end
740

741
    # Check whether the whole firewall widget ( open port checkbox
742
    # and fw details button) exists
743
    # @return [Boolean] true if both widgets exist
744
    def OpenFirewallWidgetExists
1✔
745
      UI.WidgetExists(Id("_cwm_open_firewall")) &&
×
746
        UI.WidgetExists(Id("_cwm_firewall_details"))
747
    end
748

749
    # Get the template for the help text to the firewall opening widget
750
    # @param [Boolean] restart_displayed shold be true if "Save and restart" is displayed
751
    # @return [String] help text template with %1 and %2 placeholders
752
    def OpenFirewallHelpTemplate(restart_displayed)
1✔
753
      # help text for firewall settings widget 1/3,
754
      # %1 is check box label, eg. "Open Port in Firewall" (without quotes)
755
      help = _(
×
756
        "<p><b><big>Firewall Settings</big></b><br>\n" \
757
        "To open the firewall to allow access to the service from remote computers,\n" \
758
        "set <b>%1</b>.<br>"
759
      )
760
      if restart_displayed
×
761
        # help text for firewall port openning widget 2/3, optional
762
        # %1 is push button label, eg. "Firewall &Details" (without quotes)
763
        # note: %2 is correct, do not replace with %1!!!
764
        help = Ops.add(
×
765
          help,
766
          _(
767
            "To select interfaces on which to open the port,\nclick <b>%2</b>.<br>"
768
          )
769
        )
770
      end
771
      # help text for firewall settings widget 3/3,
772
      Ops.add(
×
773
        help,
774
        _("This option is available only if the firewall\nis enabled.</p>")
775
      )
776
    end
777

778
    # Get the help text to the firewall opening widget
779
    # @param [Boolean] restart_displayed shold be true if "Save and restart" is displayed
780
    # @return [String] help text
781
    def OpenFirewallHelp(restart_displayed)
1✔
782
      Builtins.sformat(
×
783
        OpenFirewallHelpTemplate(restart_displayed),
784
        # part of help text - check box label, NO SHORTCUT!!!
785
        _("Open Port in Firewall"),
786
        # part of help text - push button label, NO SHORTCUT!!!
787
        _("Firewall Details")
788
      )
789
    end
790

791
    # Get the widget description map of the firewall enablement widget
792
    # @param [Hash{String => Object}] settings a map of all parameters needed to create the widget properly
793
    # <pre>
794
    #
795
    # - "services" : list<string> -- services identifications for the Firewall.ycp
796
    #          module
797
    # - "display_details" : boolean -- true if the details button should be
798
    #          displayed
799
    # - "firewall_details_handler" : symbol () -- function to handle the firewall
800
    #          details button. If returns something else than nil, dialog is
801
    #          exited with the returned symbol as value for wizard sequencer.
802
    #          If not specified, but "display_details" is true, common popup
803
    #          is used.
804
    # - "open_firewall_checkbox" : string -- label of the check box
805
    # - "firewall_details_button" : string -- label of the push button for
806
    #          changing firewall details
807
    # - "help" : string -- help to the widget. If not specified, generic help
808
    #          is used
809
    # </pre>
810
    # @return [Hash] the widget description map
811
    def CreateOpenFirewallWidget(settings)
1✔
812
      settings = deep_copy(settings)
6✔
813

814
      services = adapt_settings_services!(settings)
6✔
815

816
      if services.empty?
6✔
817
        log.error("Firewall services not specified") if services.empty?
2✔
818
        return { "widget" => :custom, "custom_widget" => VBox(), "help" => "" }
2✔
819
      end
820

821
      if !firewalld.installed?
4✔
822
        log.error("Firewall is not installed") if !firewalld.installed?
2✔
823
        return { "widget" => :custom, "custom_widget" => not_installed_widget, "help" => "" }
2✔
824
      end
825

826
      if services.any? { |s| !firewalld.current_service_names.include?(s) }
4✔
827
        return {
2✔
828
          "widget"        => :custom,
829
          "custom_widget" => services_not_defined_widget(services),
830
          "help"          => ""
831
        }
832
      end
833

834
      open_firewall_checkbox =
835
        settings.fetch("open_firewall_checkbox", _("Open Port in &Firewall"))
×
836
      firewall_details_button =
837
        settings.fetch("firewall_details_button", _("Firewall &Details..."))
×
838
      display_firewall_details =
839
        settings.fetch("firewall_details_handler", settings.fetch("display_details", false))
×
840

841
      help = settings.fetch("help", OpenFirewallHelp(display_firewall_details))
×
842

843
      firewall_settings = CheckBox(
×
844
        Id("_cwm_open_firewall"),
845
        Opt(:notify),
846
        open_firewall_checkbox
847
      )
848

849
      if display_firewall_details
×
850
        firewall_settings = HBox(
×
851
          firewall_settings,
852
          HSpacing(2),
853
          PushButton(Id("_cwm_firewall_details"), firewall_details_button)
854
        )
855
      end
856

857
      firewall_settings = VBox(
×
858
        Frame(
859
          format(_("Firewall Settings for %{firewall}"), firewall: Y2Firewall::Firewalld::SERVICE),
860
          VBox(
861
            Left(firewall_settings),
862
            Left(
863
              ReplacePoint(
864
                Id(:_cwm_firewall_status_rp),
865
                # label text
866
                Label(_("Firewall is open"))
867
              )
868
            )
869
          )
870
        )
871
      )
872

UNCOV
873
      {
×
874
        "widget"        => :custom,
875
        "custom_widget" => firewall_settings,
876
        "help"          => help,
877
        "init"          => fun_ref(
878
          method(:OpenFirewallInitWrapper),
879
          "void (string)"
880
        ),
881
        "store"         => fun_ref(
882
          method(:OpenFirewallStoreWrapper),
883
          "void (string, map)"
884
        ),
885
        "handle"        => fun_ref(
886
          method(:OpenFirewallHandleWrapper),
887
          "symbol (string, map)"
888
        ),
889
        "handle_events" => ["_cwm_firewall_details", "_cwm_open_firewall"]
890
      }.merge(settings)
891
    end
892

893
    # Check if settings were modified by the user
894
    # @return [Boolean] true if settings were modified
895
    def Modified
1✔
896
      firewalld.modified?
×
897
    end
898

899
    # Return the current status of the firewall related to the interfaces
900
    # opened or available
901
    #
902
    # @return [Symbol] current firewall status
903
    def current_firewall_status
1✔
904
      # bnc #429861
905
      return :not_installed if Stage.initial || !firewalld.installed?
×
906
      return :off unless firewalld.enabled?
×
907
      return :no_ifaces if all_interfaces.empty?
×
908
      return :open_all if all_interfaces.size == allowed_interfaces.size
×
909
      return :closed if allowed_interfaces.empty?
×
910

911
      :custom
×
912
    end
913

914
    # Return the firewall status label for the given status
915
    #
916
    # @param status [Symbol] current status
917
    # @return [String] current firewall status label
918
    def firewall_status_label(status)
1✔
919
      case status
1✔
920
      when :not_installed
921
        # bnc #429861
922
        if Stage.initial
1✔
923
          # label
924
          _(
×
925
            "Firewall cannot be adjusted during first stage installation."
926
          )
927
        else
928
          # label
929
          _("Firewall package is not installed.")
1✔
930
        end
931
      when :off
932
        # label
933
        _("Firewall is disabled")
×
934
      when :closed
935
        # label
936
        _("Firewall port is closed")
×
937
      when :open_all
938
        # label
939
        _("Firewall port is open on all interfaces")
×
940
      when :custom
941
        # label
942
        _("Firewall port is open on selected interfaces")
×
943
      when :no_ifaces
944
        # label
945
        _("No network interfaces are configured")
×
946
      end
947
    end
948

949
    publish function: :InitAllowedInterfaces, type: "void (list <string>)"
1✔
950
    publish function: :StoreAllowedInterfaces, type: "void (list <string>)"
1✔
951
    publish function: :InterfacesInit, type: "void (map <string, any>, string)"
1✔
952
    publish function: :InterfacesHandle, type: "symbol (map <string, any>, string, map)"
1✔
953
    publish function: :InterfacesStore, type: "void (map <string, any>, string, map)"
1✔
954
    publish function: :InterfacesValidate, type: "boolean (map <string, any>, string, map)"
1✔
955
    publish function: :InterfacesInitWrapper, type: "void (string)"
1✔
956
    publish function: :InterfacesHandleWrapper, type: "symbol (string, map)"
1✔
957
    publish function: :InterfacesStoreWrapper, type: "void (string, map)"
1✔
958
    publish function: :InterfacesValidateWrapper, type: "boolean (string, map)"
1✔
959
    publish function: :CreateInterfacesWidget, type: "map <string, any> (map <string, any>)"
1✔
960
    publish function: :DisplayDetailsPopup, type: "symbol (map <string, any>)"
1✔
961
    publish function: :OpenFirewallInit, type: "void (map <string, any>, string)"
1✔
962
    publish function: :OpenFirewallStore, type: "void (map <string, any>, string, map)"
1✔
963
    publish function: :OpenFirewallHandle, type: "symbol (map <string, any>, string, map)"
1✔
964
    publish function: :OpenFirewallInitWrapper, type: "void (string)"
1✔
965
    publish function: :OpenFirewallStoreWrapper, type: "void (string, map)"
1✔
966
    publish function: :OpenFirewallHandleWrapper, type: "symbol (string, map)"
1✔
967
    publish function: :OpenFirewallModified, type: "boolean (string)"
1✔
968
    publish function: :EnableOpenFirewallWidget, type: "void ()"
1✔
969
    publish function: :DisableOpenFirewallWidget, type: "void ()"
1✔
970
    publish function: :OpenFirewallWidgetExists, type: "boolean ()"
1✔
971
    publish function: :OpenFirewallHelpTemplate, type: "string (boolean)"
1✔
972
    publish function: :OpenFirewallHelp, type: "string (boolean)"
1✔
973
    publish function: :CreateOpenFirewallWidget, type: "map <string, any> (map <string, any>)"
1✔
974
    publish function: :Modified, type: "boolean ()"
1✔
975

976
  private
1✔
977

978
    # Return whether the '_cwm_open_firewall' widget exists or not logging the
979
    # error in case of non-existence.
980
    #
981
    # @return [Boolean] true if the open firewall widget exists
982
    def open_firewall_widget?
1✔
983
      unless UI.WidgetExists(Id("_cwm_open_firewall"))
9✔
984
        log.error("Widget _cwm_open_firewall does not exist")
1✔
985
        return false
1✔
986
      end
987

988
      true
8✔
989
    end
990

991
    # Return an instance of Y2Firewall::Firewalld
992
    #
993
    # @return [Y2Firewall::Firewalld] a firewalld instance
994
    def firewalld
1✔
995
      Y2Firewall::Firewalld.instance
20✔
996
    end
997

998
    def zone_services(services)
1✔
999
      services_status = {}
×
1000

1001
      services.each do |service|
×
1002
        service_supported = firewalld.current_service_names.include?(service)
×
1003
        services_status[service] = {}
×
1004

1005
        firewalld.zones.each do |zone|
×
1006
          next if (zone.interfaces || []).empty?
×
1007

1008
          zone.interfaces.each do |interface|
×
1009
            services_status[service][interface] =
×
1010
              service_supported ? zone.services.include?(service) : nil
×
1011
          end
1012
        end
1013
      end
1014

1015
      services_status
×
1016
    end
1017

1018
    # Return the label to show for the given interface name
1019
    #
1020
    # @param name [String] interface name
1021
    # @return [String] label for given interface name
1022
    def interface_label(name)
1✔
1023
      return name if Mode.config
×
1024

1025
      label = NetworkInterfaces.GetValue(name, "BOOTPROTO")
×
1026
      ipaddr = NetworkInterfaces.GetValue(name, "IPADDR")
×
1027
      # BNC #483455: Interface zone name
1028
      zone = firewalld.zones.find { |z| z.interfaces.include?(name) }
×
1029
      zone_full_name = zone ? zone.full_name : _("Interface is not assigned to any zone")
×
1030
      if label == "static" || label == "" || label.nil?
×
1031
        label = ipaddr
×
1032
      else
1033
        label.upcase!
×
1034
        label << "/#{ipaddr}" if !ipaddr.nil? && ipaddr != ""
×
1035
      end
1036
      if label.nil? || label == ""
×
1037
        name
×
1038
      else
1039
        "#{name} (#{label} / #{zone_full_name})"
×
1040
      end
1041
    end
1042

1043
    # Return a YaST::Term alerting that the firewall is not configurable
1044
    # because some services are not configurable and also with a list of the
1045
    # unsupported firewalld services.
1046
    #
1047
    # @return [Yast::Term] widget with a summary of supported services
1048
    def services_not_defined_widget(services)
1✔
1049
      services_list =
1050
        services.map do |service|
×
1051
          if firewalld.current_service_names.include?(service)
×
1052
            # TRANSLATORS: do not modify '%{service}', it will be replaced with service name.
1053
            # TRANSLATORS: item in a list, '-' is used as marker. Feel free to change it
1054
            HBox(HSpacing(2), Left(Label(format(_("- %{service}"), service: service))))
×
1055
          else
1056
            # TRANSLATORS: do not modify '%{service}', it will be replaced with service name.
1057
            # TRANSLATORS: item in a list, '-' is used as marker. Feel free to change it
1058
            HBox(HSpacing(2), Left(Label(format(_("- %{service} (Not available)"), service: service))))
×
1059
          end
1060
        end
1061

1062
      VBox(
×
1063
        Frame(
1064
          _("Firewall not configurable"),
1065
          VBox(
1066
            Left(Label(_("Some firewalld services are not available:"))),
1067
            *services_list,
1068
            Left(Label(_("These services must be defined in order to configure the firewall.")))
1069
          )
1070
        )
1071
      )
1072
    end
1073

1074
    # Return a YaST::Term alerting that the firewall is not configurable
1075
    # because it is not installed
1076
    #
1077
    # @return [Yast::Term] widget with not installed label
1078
    def not_installed_widget
1✔
1079
      VBox(
1✔
1080
        Frame(
1081
          _("Firewall not configurable"),
1082
          VBox(
1083
            Left(Label(firewall_status_label(:not_installed)))
1084
          )
1085
        )
1086
      )
1087
    end
1088

1089
    # Modify the services of a given widget settings definition removing old
1090
    # "services:" prepend from them.
1091
    #
1092
    # @example
1093
    #
1094
    #  settings = { "services" => ["service:apache2", "service:apache2-ssl"] }
1095
    #  adapt_settings_services!(settings) #=> ["apache2", "apache2-ssl"]
1096
    #  settings #=> { "services" => ["apache2", "apache2-ssl"] }
1097
    #
1098
    # @param settings[Hash{String => Object}] settings a map of all parameters needed
1099
    # to create the widget properly
1100
    # @return [Array<String>] converted widget settings services names
1101
    def adapt_settings_services!(settings)
1✔
1102
      services = settings.fetch("services", []) || []
6✔
1103
      return [] if services.empty?
6✔
1104

1105
      services.each { |s| s.sub!(/service:/, "") }
8✔
1106

1107
      settings["services"] = services.select { |s| s && !s.empty? }
8✔
1108
    end
1109
  end
1110

1111
  CWMFirewallInterfaces = CWMFirewallInterfacesClass.new
1✔
1112
  CWMFirewallInterfaces.main
1✔
1113
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