Mailinglist Archive: yast-devel (46 mails)

< Previous Next >
Re: [yast-devel] [PATCH 3/3] Add aa-logprof interface
On Wed, 7 Jun 2017 15:38:47 -0500
Goldwyn Rodrigues <rgoldwyn@xxxxxxx> wrote:

From: Goldwyn Rodrigues <rgoldwyn@xxxxxxxx>

The apparmor_ui_dialog communicates with aa-logprof in JSON.
Each dialog is converted to a yast dialog for input
from the user and returns the actions performed in yast in the
form of JSON response.

An extra opendialog has been used to indicate the end of the logprof
execution.

Signed-off-by: Goldwyn Rodrigues <rgoldwyn@xxxxxxxx>
---
src/clients/apparmor.rb | 1 +
src/clients/logprof.rb | 94 +++++++++++++
src/lib/apparmor/apparmor_ui_dialog.rb | 243
+++++++++++++++++++++++++++++++++
3 files changed, 338 insertions(+)
create mode 100644 src/clients/logprof.rb
create mode 100644 src/lib/apparmor/apparmor_ui_dialog.rb

diff --git a/src/clients/apparmor.rb b/src/clients/apparmor.rb
index 1e6d7e3..7c2ed6f 100644
--- a/src/clients/apparmor.rb
+++ b/src/clients/apparmor.rb
@@ -70,6 +70,7 @@ module Yast
[
# Selection box items
Item(Id("apparmor-settings"), _("Settings"), true),
+ Item(Id("logprof"), _("Scan Audit logs")),
Item(Id("AA_AddProfile"), _("Manually Add Profile"))
]
),
diff --git a/src/clients/logprof.rb b/src/clients/logprof.rb
new file mode 100644
index 0000000..95cbf5c
--- /dev/null
+++ b/src/clients/logprof.rb
@@ -0,0 +1,94 @@
+
+require "open3"
+require "json"
+require "yast"
+require "apparmor/apparmor_ui_dialog"
+
+Yast.import "UI"
+Yast.import "Label"
+Yast.import "Popup"
+
+module AppArmor
+ class LogProf
+ LOGPROF = "/usr/sbin/aa-logprof"
+ attr_reader :logfile, :raw
+ include Yast::UIShortcuts
+ include Yast::Logger
+ include Yast::I18n
+
+ def initialize(logfile="")
+ @logfile = logfile
+ log.info "Logfile is #{logfile}."
+ end
+
+ def execute()
+ cmd = "#{LOGPROF} --json"
+ if @logfile.length > 0
+ cmd += " -f #{@logfile}"
+ end
+ log.info "Executing #{cmd}"
+ IO.popen(cmd, "r+") do |f|
+ f.sync = true
+ f.each do | line |
+ log.info "aa-logprof lines #{line}."
+ if !line.start_with?("{")
+ next
+ end
+ hm = JSON.parse(line)
+ log.info "aa-logprof hashmap #{hm}."
+ l = get_dialog(hm)
+ r = l.run
+ if !r.nil?
+ f.puts r.to_json
+ f.flush
+ end
+ end
+ end
+
+ Yast::UI.OpenDialog(
+ Opt(:decorated, :defaultsize),
+ VBox(
+ Label(_("No more records in logfile #{@logfile} to process")),
+ VSpacing(2),
+ HBox(
+ HStretch(),
+ HWeight(1, PushButton(Id(:ok), Yast::Label.OKButton)),
+ HStretch()
+ )
+ )
+ )
+ Yast::UI.UserInput()
+ Yast::UI.CloseDialog()
+ end
+
+ private
+ def get_dialog(hm)
+ case hm["dialog"]
+ when "yesno"
+ return YesNoDialog.new(hm)
+ when "yesnocancel"
+ return YesNoCancelDialog.new(hm)
+ when "info"
+ return InfoDialog.new(hm)
+ when "getstring"
+ return GetStringDialog.new(hm)
+ when "getfile"
+ return GetFileDialog.new(hm)
+ when "getstring"
+ return PromptDialog.new(hm)
+ when "promptuser"
+ return PromptDialog.new(hm)
+ when "apparmor-json-version"
+ return AAJSONVersion.new(hm)
+ else
+ Yast::Report.Error(_("Unknown Dialog %s returned by apparmor") %
hm["dialog"])
+ return nil
+ end
+ end
+ end
+end
+
+#AppArmor::LogProf.new.execute
+AppArmor::LogProf.new("/var/log/audit/audit.log").execute
+
+
diff --git a/src/lib/apparmor/apparmor_ui_dialog.rb
b/src/lib/apparmor/apparmor_ui_dialog.rb
new file mode 100644
index 0000000..8045301
--- /dev/null
+++ b/src/lib/apparmor/apparmor_ui_dialog.rb
@@ -0,0 +1,243 @@
+# This file has all the dialog translations/representations
+# which come from the apparmor's json
+# show_respond() must return what would be communicated back to
+# the program in the form of a hash map. It could be nil if no
+# data must be written.
+
+require "yast"
+
+Yast.import "UI"
+Yast.import "Label"
+Yast.import "Report"
+Yast.import "Popup"
+
+module AppArmor
+ class YesNoDialog
+ include Yast::UIShortcuts
+ include Yast::I18n
+ def initialize(hm)
+ @data = hm["text"]
+ @map = Hash.new
+ @map["dialog"] = "yesno"
+ end
+
+ def run()
+ Yast::UI.OpenDialog(
+ Opt(:decorated, :defaultsize),
+ VBox(
+ Label(@data),
+ VSpacing(1),
+ HBox(
+ HStretch(),
+ HWeight(1, PushButton(Id(:y), _("Yes"))),
+ HSpacing(2),
+ HWeight(1, PushButton(Id(:n), _("No"))),
+ )
+ )
+ )
+ case Yast::UI.UserInput
+ when :y
+ @map["response"] = "yes"
+ @map["response_key"] = "y"
+ when :n
+ @map["response"] = "no"
+ @map["response_key"] = "n"
+ end
+ Yast::UI.CloseDialog
+ return @map
+ end
+ end

how big data you expect? In general maybe easier way is to simply use
Yast::Popup.YesNo
http://www.rubydoc.info/github/yast/yast-yast2/Yast/PopupClass#YesNo-instance_method


+
+ class YesNoCancelDialog
+ include Yast::UIShortcuts
+ include Yast::I18n
+ include Yast::Logger
+ def initialize(hm)
+ @data = hm["data"]
+ @map = Hash.new
+ @map["dialog"] = "yesnocancel"
+ end
+
+ def run()
+ Yast::UI.OpenDialog(
+ Opt(:decorate, :defaultsize),
+ VBox(
+ Label(@data),
+ VSpacing(0.3),
+ HBox(
+ HWeight(1, PushButton(Id(:y), _("Yes"))),
+ HStretch(),
+ HWeight(1, PushButton(Id(:n), _("No"))),
+ HStretch(),
+ HWeight(1, PushButton(Id(:c), Label.CancelButton))
+ )
+ )
+ )
+ case Yast::UI.UserInput
+ when :y
+ @map["response"] = "yes"
+ @map["response_key"] = "y"
+ when :n
+ @map["response"] = "no"
+ @map["response_key"] = "n"
+ when :c
+ @map["response"] = "cancel"
+ @map["response_key"] = "c"
+ end
+ Yast::UI.CloseDialog
+ return @map
+ end
+ end

Here I would reuse Popup.AnyQuestion3
http://www.rubydoc.info/github/yast/yast-yast2/Yast%2FPopupClass:AnyQuestion3

+
+ class InfoDialog
+ include Yast::UIShortcuts
+ include Yast::I18n
+ include Yast::Logger
+ def initialize(hm)
+ log.info "Hash map #{hm}"
+ end
+ def run()
+ return nil
+ end
+ end

Ugh, what? what is purpose of this dialog? It do nothing.

+
+ class GetStringDialog
+ include Yast::UIShortcuts
+ include Yast::I18n
+ include Yast::Logger
+ def initialize(hm)
+ log.info "Hash map #{hm}"
+ @map = Hash.new
+ @map["dialog"] = "getstring"
+ end
+ def run()
+ Yast::UI.OpenDialog(
+ Opt(:decorate, :defaultsize),
+ VBox(
+ Inputfield(Id(:str), @text),
+ PushButton("&OK")
+ )
+ )
+ str = Yast::UI.QueryWidget(Id(:str), :Value)
+ Yast::UI.UserInput()
+ Yast::UI.CloseDialog()
+ @map["response"] = str
+ return @map
+ end
+ end
+
+ class GetFileDialog
+ include Yast::UIShortcuts
+ include Yast::I18n
+ include Yast::Logger
+ def initialize(hm)
+ log.info "Hash map #{hm}"
+ @map = Hash.new
+ @map["dialog"] = "getfile"
+ end
+ def run()
+ filename = Yast::UI.AskForExistingFile("/etc/apparmor.d", "*",
_("Choose a file"))
+ if !filename.nil?
+ @map["response"] = filename
+ end
+ return @map
+ end
+
+ end
+
+ class PromptDialog
+ include Yast::UIShortcuts
+ include Yast::I18n
+ include Yast::Logger
+ include Yast
+ def initialize(hm)
+ log.info "Hash map #{hm}"
+ @map = Hash.new
+ @map["dialog"] = "promptuser"
+ @title = hm["title"]
+ @headers = hm["headers"]
+ @explanation = hm["explanation"]
+ @options = hm["options"]
+ @menu_items = hm["menu_items"]
+ end
+ def run()
+ @map["selected"] = 0
+ # Display the dynamic widget
+
+ UI.OpenDialog(
+ Opt(:decorated, :defaultsize),
+ VBox(
+ *explanation_label,
+ *header_labels,
+ *options_radiobtns,
+ *menubtns
+ )
+ )
+
+ @map["response_key"] = Yast::UI.UserInput()
+ selected = Yast::UI.QueryWidget(:options, :CurrentButton)
+ @map["selected"] = selected.to_i
+ return @map
+ end
+ private
+
+ def explanation_label
+ box = VBox()
+ box[box.size] = VSpacing(1)

term have << method
http://www.rubydoc.info/github/yast/yast-ruby-bindings/Yast/Term#%3C%3C-instance_method
but here. I suggest to simply have
```
box = VBox(VSpacing(1))
```

+ if !@explanation.nil?
+ box[box.size] = Label(@explanation)
and here
```
box << Label(@explanation) if @explanation
```
+ end
+ box
+ end
+
+ def
+ box = VBox()
+ @headers.each do | key, value |
+ box[box.size] = Label(key.to_s + ": " + value.to_s)
+ box[box.size] = VSpacing(1)

same here. << operator is easier to read.

+ end
+ box

btw whole method can be done using each_with_object like

```
def header_labels
@headers.each_with_object(VBox()) do |pair, result|
key, value = pair
result << Label(key.to_s + ": " + value.to_s)
result << VSpacing(1)
end
end
```

+ end
+
+ def options_radiobtns

typo here

+ box = VBox()
+ @options.each_with_index do | opt, i|
+ box[box.size] = RadioButton(Id(i.to_s), opt, i==0)
+ box[box.size] = VSpacing(1)
+ end
+ VBox(RadioButtonGroup(Id(:options), box))
+ end
+ def menu_to_text_key(menu)
+ ind = menu.index('(')
+ key = menu[ind+1, 1].downcase
+ text = menu.delete('(').delete(')').delete('[').delete(']')
+ return text, key
+ end
+ def menubtns
+ box = HBox()
+ @menu_items.each do | menu |
+ text, key = menu_to_text_key(menu)
+ box[box.size] = HWeight(1, PushButton(Id(key.to_s), text))
+ box[box.size] = HSpacing(1)
+ end
+ VBox(box)
+ end
+ end
+
+ class AAJSONVersion
+ include Yast::I18n
+ include Yast::Logger
+ AA_JSON = 2.12

I expect this will break often in future. Is there any plans to have json
somehow backward compatible?

+ def initialize(hm)
+ log.info "Hash map #{hm}"
+ @json_version = hm["data"].to_f
+ if (@json_version > AA_JSON)
+ Yast::Report.Error(_("Apparmor JSON version %s is greater than
%0.2f" % [@json_version, AA_JSON]))
+ end
+ end
+ def run()
+ return nil
+ end
+ end
+end
+

And that is all, a lot of code, some parts looks really interesting. Good that
you send it for early review. Do not hesitate to contact us on IRC if you have
any questions.

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

< Previous Next >
Follow Ups