On Wed, 7 Jun 2017 15:38:46 -0500 Goldwyn Rodrigues <rgoldwyn@suse.de> wrote:
From: Goldwyn Rodrigues <rgoldwyn@suse.com>
Handles the profiles using JSON output of aa-status. This replaces the earlier way of handling profiles.
Signed-off-by: Goldwyn Rodrigues <rgoldwyn@suse.com> --- 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@opensuse.org To contact the owner, e-mail: yast-devel+owner@opensuse.org