Mailinglist Archive: yast-devel (46 mails)

< Previous Next >
Re: [yast-devel] [PATCH 2/3] Handle profiles
On Wed, 7 Jun 2017 15:38:46 -0500
Goldwyn Rodrigues <rgoldwyn@xxxxxxx> wrote:

From: Goldwyn Rodrigues <rgoldwyn@xxxxxxxx>

Handles the profiles using JSON output of aa-status. This replaces the earlier
way of handling profiles.

Signed-off-by: Goldwyn Rodrigues <rgoldwyn@xxxxxxxx>
---
src/clients/AA_EditProfile.rb | 129 ----------------------
src/clients/apparmor-settings.rb | 3 +-
src/clients/apparmor.rb | 1 -
src/clients/profiles.rb | 111 +++++++++++++++++++
src/lib/apparmor/profiles.rb | 227
+++++++++++++++++++++++++++++++++++++++
5 files changed, 340 insertions(+), 131 deletions(-)
delete mode 100644 src/clients/AA_EditProfile.rb
create mode 100644 src/clients/profiles.rb
create mode 100644 src/lib/apparmor/profiles.rb

diff --git a/src/clients/AA_EditProfile.rb b/src/clients/AA_EditProfile.rb
deleted file mode 100644
index a616143..0000000
--- a/src/clients/AA_EditProfile.rb
+++ /dev/null
@@ -1,129 +0,0 @@
-# encoding: utf-8
-
-# ***************************************************************************
-#
-# Copyright (c) 2002 - 2012 Novell, Inc.
-# All Rights Reserved.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of version 2 of the GNU General Public License as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, contact Novell, Inc.
-#
-# To contact Novell about this file by physical or electronic mail,
-# you may find current contact information at www.novell.com
-#
-# ***************************************************************************
-module Yast
- class AAEditProfileClient < Client
- def main
- Yast.import "UI"
- Yast.import "Wizard"
- Yast.import "Popup"
- Yast.import "Label"
- Yast.import "Sequencer"
- Yast.include self, "apparmor/apparmor_packages.rb"
- Yast.include self, "apparmor/apparmor_profile_check.rb"
- Yast.include self, "apparmor/profile_dialogs.rb"
- textdomain "yast2-apparmor"
-
- # Globalz
-
- @profiles = nil
-
-
-
- #
- # YEAH BABY RUN BABY RUN
- #
- @ret = nil
-
- # no command line support #269891
- if Ops.greater_than(Builtins.size(WFM.Args), 0)
- Yast.import "CommandLine"
- CommandLine.Init({}, WFM.Args)
- return deep_copy(@ret)
- end
-
- return deep_copy(@ret) if !installAppArmorPackages
-
- return deep_copy(@ret) if !checkProfileSyntax
-
-
- @ret = MainSequence()
- deep_copy(@ret)
- end
-
- def Reread
- @profiles = Convert.to_map(SCR.Read(path(".apparmor_profiles"), "all"))
- :next
- end
-
-
- def MainSequence
- #
- # Read the profiles from the SCR agent
- Reread()
-
- aliases = {
- "showProfile" => lambda do
- DisplayProfileForm(
- Ops.get_string(@Settings, "CURRENT_PROFILE", ""),
- false
- )
- end,
- "showHat" => lambda do
- DisplayProfileForm(Ops.get_string(@Settings, "CURRENT_HAT", ""),
true)
- end,
- "chooseProfile" => lambda do
- SelectProfileForm(
- @profiles,
- _("Select a listed profile and press Edit to edit it."),
- _("Edit Profile - Choose profile to edit"),
- "apparmor_edit_profile"
- )
- end,
- "reread" => lambda { Reread() }
- }
-
- sequence = {
- "ws_start" => "chooseProfile",
- "chooseProfile" => {
- :abort => :abort,
- :edit => "showProfile",
- :reread => "reread",
- :next => :next
- },
- "showProfile" => {
- :abort => :abort,
- :next => "reread",
- :showhat => "showHat",
- :finish => :next
- },
- "reread" => { :next => "chooseProfile" },
- "showHat" => {
- :abort => :abort,
- :next => "showProfile",
- :finish => :next
- }
- }
-
- Wizard.CreateDialog
- Wizard.SetTitleIcon("apparmor_edit_profile")
- ret = Sequencer.Run(aliases, sequence)
- Wizard.CloseDialog
- @Settings = Builtins.remove(@Settings, "CURRENT_PROFILE")
- @Settings = Builtins.remove(@Settings, "PROFILE_MAP")
- deep_copy(ret)
- end
- end
-end
-
-Yast::AAEditProfileClient.new.main
diff --git a/src/clients/apparmor-settings.rb
b/src/clients/apparmor-settings.rb
index 2b2d3f2..d3f6d1a 100644
--- a/src/clients/apparmor-settings.rb
+++ b/src/clients/apparmor-settings.rb
@@ -21,6 +21,7 @@
# ***************************************************************************

require "yast"
+require "apparmor/profiles"

Yast.import "UI"
Yast.import "Label"
@@ -77,7 +78,7 @@ module AppArmor
loop do
case Yast::UI.UserInput
when :modeconf
- break
+ AppArmor::ProfilesDialog.new.run
when :quit
break
end
diff --git a/src/clients/apparmor.rb b/src/clients/apparmor.rb
index 63e8874..1e6d7e3 100644
--- a/src/clients/apparmor.rb
+++ b/src/clients/apparmor.rb
@@ -70,7 +70,6 @@ module Yast
[
# Selection box items
Item(Id("apparmor-settings"), _("Settings"), true),
- Item(Id("AA_EditProfile"), _("Manage Existing Profiles")),
Item(Id("AA_AddProfile"), _("Manually Add Profile"))
]
),
diff --git a/src/clients/profiles.rb b/src/clients/profiles.rb
new file mode 100644
index 0000000..06c6e8a
--- /dev/null
+++ b/src/clients/profiles.rb
@@ -0,0 +1,111 @@
+
+require "apparmor/profiles"
+Yast.import "UI"
+Yast.import "Label"
+Yast.import "Popup"
+
+class ProfilesDialog
+ include Yast::UIShortcuts
+ include Yast::I18n
+ include Yast::Logger
+
+ def initialize
+ @profiles = Apparmor::Profiles.new
+ @active = true
+ end
+
+ def run
+ return unless create_dialog
+
+ begin
+ return event_loop
+ ensure
+ close_dialog
+ end
+ end
+
+private
+ def create_dialog
+ Yast::UI.OpenDialog(
+ Opt(:decorated, :defaultsize),
+ VBox(
+ #Header
+ Heading(_("Profile List")),
+ # Active profiles
+ Left(
+ CheckBox(Id(:active_only), Opt(:notify), _("Show Active only"),
@active)
+ ),
+ VSpacing(0.4),
+ # Profile List
+ table,
+ VSpacing(0.3),
+ # Footer buttons
+ HBox(
+ HWeight(1, PushButton(Id(:toggle), _("Toggle mode"))),
+ HStretch(),
+ HWeight(1, PushButton(Id(:add), Yast::Label.AddButton)),
+ HStretch(),
+ HWeight(1, PushButton(Id(:edit), Yast::Label.EditButton)),
+ HStretch(),
+ HWeight(1, PushButton(Id(:finish), Yast::Label.FinishButton))
+ )
+ )
+ )
+ end
+
+ def table
+ headers = Array[_("Name"), _("Mode"), _("PID")]
+ Table(
+ Id(:entries_table),
+ Opt(:keepSorting),
+ Header(*headers),
+ table_items
+ )
+ end
+
+ def table_items
+ if @active
+ profs = @profiles.active
+ else
+ profs = @profiles.all
+ end
+ arr = Array.new
+ profs.each do | n, pr |
+ arr.push(pr.to_array)
+ end
+ arr.map { |i| Item(*i) }
+ end
+
+ def redraw_table
+ Yast::UI.ChangeWidget(Id(:entries_table), :Items, table_items)
+ end
+
+ def event_loop
+ loop do
+ case Yast::UI.UserInput
+ when :active_only
+ @active = Yast::UI.QueryWidget(Id(:active_only), :Value)
+ redraw_table
+ when :cancel
+ break
+ when :toggle
+ selectedItem = Yast::UI.QueryWidget(Id(:entries_table), :CurrentItem)
+ log.info "Toggling #{selectedItem}"
+ @profiles.toggle(selectedItem)
+ redraw_table
+ when :edit
+ break
+ when :finish
+ break
+ end
+ end
+ end
+
+ def close_dialog
+ Yast::UI.CloseDialog
+ end
+end
+
+ProfilesDialog.new.run
+
+
diff --git a/src/lib/apparmor/profiles.rb b/src/lib/apparmor/profiles.rb
new file mode 100644
index 0000000..f3129f6
--- /dev/null
+++ b/src/lib/apparmor/profiles.rb
@@ -0,0 +1,227 @@
+# Get the status of profiles loaded
+# - enforced
+# - complain
+# Uses aa-status --json
+
+require "json"
+require "open3"
+Yast.import "UI"
+Yast.import "Label"
+Yast.import "Popup"
+
+module AppArmor
+
+ class Profile
+ attr_reader :name, :status, :pid
+
+ def initialize(name, status)
+ @name=name
+ @status=status
+ @pid = Array.new
+ end
+
+ #Return/Set the name of the profile
+ def name(n="")
+ if n.length > 0
+ @name = n
+ end
+ return @name
+ end
+
+ #return the current status
+ def status(s="")
+ if s != ""
+ @status = s
+ end
+ return @status
+ end
+
+ # Set to complain mode
+ def complain()
+ system("/usr/sbin/aa-complain #{@name}")
+ return self.status("complain")
+ end
+
+ # Set to enforce mode
+ def enforce()
+ system("/usr/sbin/aa-enforce #{@name}")
+ return self.status("enforce")
+ end
+
+ def addPid(p)
+ @pid.push(p)
+ end
+
+ def pid
+ return @pid
+ end
+
+ def toggle
+ if @status == "complain"
+ self.enforce
+ else
+ self.complain
+ end
+ end
+
+ def to_s
+ @name + ", " + @status + ", " + @pid
+ end
+
+ def to_array
+ a = Array.new
+ a.push(@name)
+ a.push(@status)
+ pstr = " "
+ # Convert PID array to a pretty string with commas
+ @pid.each do | p |
+ pstr = pstr + p.to_str + ", "
+ end
+ a.push(pstr)
+ return a
+ end
+ end
+
+ class ProfileMap
+ def self.all(text)
+ jtext = JSON.parse(text)
+ h = jtext["profiles"]
+ entries = Hash.new
+ h.each do |name, status|
+ entries[name] = Profile.new(name, status)
+ end
+ h = jtext["processes"]
+ h.each do |name, pidmap|
+ pidmap.each do |p|
+ entries[name].addPid(p["pid"])
+ end
+ end
+ return entries
+ end
+ end
+
+ class Profiles
+ STATUS_CMD = "sudo /usr/sbin/aa-status --json"
+ attr_reader :raw, :prof
+ def initialize()
+ cmd = "#{STATUS_CMD}"
+ stdin, stdout, stderr, wait_thr = Open3.popen3(cmd)
+ @prof = ProfileMap.all(stdout.read)
+ end
+
+ def remove(name)
+ e = @prof.delete(name)
+ # execute file deletion?
+ end
+
+ def active()
+ # Select the ones which have pids
+ @prof.select { | name, pr | pr.pid.length > 0 }
+ end
+
+ def all()
+ return @prof
+ end
+
+ def toggle(name)
+ @prof[name].toggle
+ end
+ end
+
+ class ProfilesDialog
+ include Yast::UIShortcuts
+ include Yast::I18n
+ include Yast::Logger
+
+ def initialize
+ @profiles = Profiles.new
+ @active = true
+ end
+
+ def run
+ return unless create_dialog
+
+ begin
+ return event_loop
+ ensure
+ close_dialog
+ end
+ end

As you can see almost identical code for dialog, so using dialog base really
safes you typing and it is usually less buggy, as it is used and tested more
heavily.

+
+ private
+ def create_dialog
+ Yast::UI.OpenDialog(
+ Opt(:decorated, :defaultsize),
+ VBox(
+ #Header
+ Heading(_("Profile List")),
+ # Active profiles
+ Left(
+ CheckBox(Id(:active_only), Opt(:notify), _("Show Active only"),
@active)
+ ),
+ VSpacing(0.4),
+ # Profile List
+ table,
+ VSpacing(0.3),
+ # Footer buttons
+ HBox(
+ HWeight(1, PushButton(Id(:changeMode), _("Change mode"))),
+ HStretch(),
+ HWeight(1, PushButton(Id(:finish), Yast::Label.FinishButton))
+ )
+ )
+ )
+ end
+
+ def table
+ headers = Array[_("Name"), _("Mode"), _("PID")]
+ Table(
+ Id(:entries_table),
+ Opt(:keepSorting),
+ Header(*headers),

nice ;)

+ table_items
+ )
+ end
+
+ def table_items
+ if @active
+ profs = @profiles.active
+ else
+ profs = @profiles.all
+ end
+ arr = Array.new
+ profs.each do | n, pr |
+ arr.push(pr.to_array)
+ end
+ arr.map { |i| Item(*i) }

I think you can write it as
```
profs.values.map { |i| Item(*i.to_array) }
```
but in email it is not so easy to see content and maybe Profiles provide even
nicer api for this.
+ end
+
+ def redraw_table
+ Yast::UI.ChangeWidget(Id(:entries_table), :Items, table_items)
+ end
+
+ def event_loop
+ loop do
+ case Yast::UI.UserInput
+ when :active_only
+ @active = Yast::UI.QueryWidget(Id(:active_only), :Value)
+ redraw_table
+ when :cancel
+ break
+ when :changeMode
+ selectedItem = Yast::UI.QueryWidget(Id(:entries_table),
:CurrentItem)
+ log.info "Toggling #{selectedItem}"
+ @profiles.toggle(selectedItem)
+ redraw_table

in email indentation is a bit broken, but if you will use rubocop, It will be
catched automatic, so I do not need to check it myself.

+ when :finish
+ break

you can write it together with cancel:

```
when :cancel, :finish
break
```

and second part. Problem here is that cancel and finish act same. No
difference. I expect users will be confused by it.

+ end
+ end
+ end
+
+ def close_dialog
+ Yast::UI.CloseDialog
+ end
+ end
+end
+

--
To unsubscribe, e-mail: yast-devel+unsubscribe@xxxxxxxxxxxx
To contact the owner, e-mail: yast-devel+owner@xxxxxxxxxxxx

< Previous Next >