synerma-audits1984 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +77 -0
  4. data/Rakefile +19 -0
  5. data/app/assets/config/audits1984_manifest.js +2 -0
  6. data/app/assets/stylesheets/audits1984/application.css +15 -0
  7. data/app/assets/stylesheets/audits1984/bulma.min.css +1 -0
  8. data/app/assets/stylesheets/audits1984/forms.css +3 -0
  9. data/app/assets/stylesheets/audits1984/sessions.css +5 -0
  10. data/app/controllers/audits1984/application_controller.rb +25 -0
  11. data/app/controllers/audits1984/audits_controller.rb +43 -0
  12. data/app/controllers/audits1984/filtered_sessions_controller.rb +15 -0
  13. data/app/controllers/audits1984/filtered_sessions_scoped.rb +14 -0
  14. data/app/controllers/audits1984/sessions_controller.rb +16 -0
  15. data/app/helpers/audits1984/application_helper.rb +38 -0
  16. data/app/javascript/audits1984/application.js +2 -0
  17. data/app/jobs/audits1984/application_job.rb +4 -0
  18. data/app/mailers/audits1984/application_mailer.rb +6 -0
  19. data/app/models/audits1984/application_record.rb +5 -0
  20. data/app/models/audits1984/audit.rb +10 -0
  21. data/app/models/audits1984/current.rb +3 -0
  22. data/app/models/audits1984/filtered_sessions.rb +30 -0
  23. data/app/models/audits1984/session/auditable.rb +13 -0
  24. data/app/models/audits1984/session/iterable.rb +23 -0
  25. data/app/views/audits1984/sessions/_audit_form.html.erb +26 -0
  26. data/app/views/audits1984/sessions/_executed_code.html.erb +26 -0
  27. data/app/views/audits1984/sessions/_filter.html.erb +34 -0
  28. data/app/views/audits1984/sessions/_header.html.erb +14 -0
  29. data/app/views/audits1984/sessions/_session.html.erb +25 -0
  30. data/app/views/audits1984/sessions/_summary.html.erb +5 -0
  31. data/app/views/audits1984/sessions/index.html.erb +6 -0
  32. data/app/views/audits1984/sessions/show.html.erb +10 -0
  33. data/app/views/layouts/audits1984/_flash.html.erb +10 -0
  34. data/app/views/layouts/audits1984/application.html.erb +24 -0
  35. data/config/importmap.rb +2 -0
  36. data/config/routes.rb +9 -0
  37. data/db/migrate/20210810092639_create_auditing_tables.rb +12 -0
  38. data/lib/audits1984/engine.rb +46 -0
  39. data/lib/audits1984/version.rb +3 -0
  40. data/lib/audits1984.rb +13 -0
  41. data/lib/tasks/audits1984_tasks.rake +4 -0
  42. metadata +324 -0
@@ -0,0 +1,38 @@
1
+ require "rouge"
2
+
3
+ module Audits1984
4
+ module ApplicationHelper
5
+ include Audits1984::Engine.routes.url_helpers
6
+ def auditor_name_for(auditor)
7
+ if auditor == Audits1984::Current.auditor
8
+ "You"
9
+ else
10
+ auditor.public_send(Audits1984.auditor_name_attribute)
11
+ end
12
+ end
13
+
14
+ def format_date(date)
15
+ # <time datetime="2016-1-1">11:09 PM - 1 Jan 2016</time>
16
+ date.strftime("%Y-%m-%d")
17
+ end
18
+
19
+ def format_date_and_time(date)
20
+ # <time datetime="2016-1-1">11:09 PM - 1 Jan 2016</time>
21
+ date.strftime("%Y-%m-%d at %I:%m %P")
22
+ end
23
+
24
+ def highlighted_code_from(commands)
25
+ highlight_code commands.collect(&:statements).collect(&:strip).filter(&:present?).join("\n")
26
+ end
27
+
28
+ def highlight_code(source)
29
+ formatter = Rouge::Formatters::HTMLLinewise.new(Rouge::Formatters::HTML.new)
30
+ lexer = Rouge::Lexers::Ruby.new
31
+ formatter.format(lexer.lex(source)).html_safe
32
+ end
33
+
34
+ def sensitive_session_decoration(session)
35
+ "*" if session.sensitive?
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,2 @@
1
+ // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
2
+ import "@hotwired/turbo-rails"
@@ -0,0 +1,4 @@
1
+ module Audits1984
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Audits1984
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: "from@example.com"
4
+ layout "mailer"
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Audits1984
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ module Audits1984
2
+ class Audit < Console1984::Base
3
+ belongs_to :session, class_name: "Console1984::Session", touch: true
4
+ belongs_to :auditor, class_name: Audits1984.auditor_class
5
+
6
+ enum :status, %i[ pending approved flagged ]
7
+
8
+ encrypts :notes
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ class Audits1984::Current < ActiveSupport::CurrentAttributes
2
+ attribute :auditor
3
+ end
@@ -0,0 +1,30 @@
1
+ module Audits1984
2
+ class FilteredSessions
3
+ include ActiveModel::Model
4
+ include ActiveModel::Attributes
5
+
6
+ attribute :from_date, :date
7
+ attribute :to_date, :date
8
+ attribute :sensitive_only, :boolean
9
+
10
+ def self.resume(attributes)
11
+ new attributes&.with_indifferent_access&.slice(*attribute_names)
12
+ end
13
+
14
+ def to_h
15
+ attributes.compact.transform_values(&:to_s)
16
+ end
17
+
18
+ def all
19
+ sessions = Console1984::Session.order(created_at: :desc, id: :desc)
20
+ sessions = sessions.sensitive if sensitive_only
21
+ sessions = sessions.where("console1984_sessions.created_at >= ?", from_date.beginning_of_day) if from_date.present?
22
+ sessions = sessions.where("console1984_sessions.created_at <= ?", to_date.end_of_day) if to_date.present?
23
+ sessions
24
+ end
25
+
26
+ def pending_session_after(session)
27
+ all.pending.where("console1984_sessions.created_at < ? OR console1984_sessions.id < ?", session.created_at, session.id).first
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ module Audits1984::Session::Auditable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ has_many :audits, dependent: :destroy, class_name: "Audits1984::Audit"
6
+
7
+ scope :sensitive, -> { joins(:sensitive_accesses).distinct }
8
+ scope :reviewed, -> { joins(:audits).distinct }
9
+ scope :approved, -> { reviewed.where("audits.status": :approved) }
10
+ scope :flagged, -> { reviewed.where("audits.status": :flagged) }
11
+ scope :pending, -> { where.not(id: reviewed.distinct(false)) }
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ module Audits1984::Session::Iterable
2
+ extend ActiveSupport::Concern
3
+
4
+ # Loops through all the session commands in order, yielding lists grouped by
5
+ # its sensitive access record, or its absence
6
+ def each_batch_of_commands_grouped_by_sensitive_access
7
+ group = []
8
+ current_sensitive_access = nil
9
+ commands.includes(:sensitive_access).sorted_chronologically.each.with_index do |command, index|
10
+ current_sensitive_access = command.sensitive_access if index == 0
11
+ if index > 0 && command.sensitive_access != current_sensitive_access
12
+ yield current_sensitive_access, group
13
+ group = []
14
+ current_sensitive_access = command.sensitive_access
15
+ end
16
+ group << command
17
+ end
18
+
19
+ if group.present?
20
+ yield current_sensitive_access, group
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ <article class="panel mt-5">
2
+ <p class="panel-heading">
3
+ Your review
4
+ </p>
5
+
6
+ <div class="panel-block">
7
+ <%= form_with model: [session, audit], authenticity_token: true do |form| %>
8
+ <div class="field">
9
+ <div class="control is-fullwidth">
10
+ <%= form.label :from_date, "Notes" %>
11
+ <%= form.text_area :notes, class: "textarea", placeholder: "Enter optional comments..." %>
12
+ </div>
13
+ </div>
14
+ <div class="field is-grouped">
15
+ <div class="control">
16
+ <%= form.button name: "audit[status]", value: :approved, class: "button is-primary" do %>
17
+ Approve
18
+ <% end %>
19
+ <%= form.button name: "audit[status]", value: :flagged, class: "button is-danger" do %>
20
+ Flag as inappropriate
21
+ <% end %>
22
+ </div>
23
+ </div>
24
+ <% end %>
25
+ </div>
26
+ </article>
@@ -0,0 +1,26 @@
1
+ <style>
2
+ <%= Rouge::Theme.find('github').render(scope: '.highlight') %>
3
+ </style>
4
+
5
+ <% session.each_batch_of_commands_grouped_by_sensitive_access do |sensitive_access, commands| %>
6
+ <div class="card">
7
+ <div class="card-content">
8
+ <div class="media">
9
+ <div class="media-content">
10
+ <% if sensitive_access %>
11
+ <p class="title is-4">Sensitive access *</p>
12
+ <p class="subtitle is-6"><%= Rinku.auto_link(sensitive_access.justification).html_safe %></p>
13
+ <% else %>
14
+ <p class="title is-4">Protected access</p>
15
+ <% end %>
16
+ </div>
17
+ </div>
18
+
19
+ <div class="content">
20
+ <pre class="highlight">
21
+ <%= highlighted_code_from commands %>
22
+ </pre>
23
+ </div>
24
+ </div>
25
+ </div>
26
+ <% end %>
@@ -0,0 +1,34 @@
1
+ <nav class="panel">
2
+ <p class="panel-heading">
3
+ Filter console sessions
4
+ </p>
5
+ <div class="panel-block">
6
+
7
+ <%= form_with model: filtered_sessions, url: filtered_sessions_path, method: :put, authenticity_token: true do |form| %>
8
+ <div class="field is-grouped">
9
+ <div class="control">
10
+ <%= form.label :from_date, "From" %>
11
+ <%= form.date_field :from_date, class: "input" %>
12
+ </div>
13
+
14
+ <div class="control">
15
+ <%= form.label :to_date, "To" %>
16
+ <%= form.date_field :to_date, class: "input" %>
17
+ </div>
18
+ </div>
19
+
20
+ <div class="field">
21
+ <div class="control">
22
+ <%= form.label :sensitive_only, "Only with sensitive access", class: "checkbox" %>
23
+ <%= form.check_box :sensitive_only %>
24
+ </div>
25
+ </div>
26
+
27
+ <div class="field is-grouped">
28
+ <div class="control">
29
+ <%= form.submit "Filter", class: "button is-link" %>
30
+ </div>
31
+ </div>
32
+ <% end %>
33
+ </div>
34
+ </nav>
@@ -0,0 +1,14 @@
1
+ <h2 class="title"><%= Rinku.auto_link(session.reason).html_safe %> <%= sensitive_session_decoration(session) %></h2>
2
+ <h3 class="subtitle">by <%= session.user.username %>
3
+ <br> <%= format_date_and_time(session.created_at) %></h3>
4
+
5
+ <% if session.audits.any? %>
6
+ <ul class="mb-6">
7
+ <% session.audits.except(&:new_record?).each do |audit| %>
8
+ <li> <%= audit.status.humanize %>
9
+ by <%= auditor_name_for(audit.auditor) %>
10
+ on <%= format_date_and_time(audit.created_at) %>
11
+ </li>
12
+ <% end %>
13
+ </ul>
14
+ <% end %>
@@ -0,0 +1,25 @@
1
+ <%= link_to session_path(session) do %>
2
+ <article class="session media">
3
+ <div class="media-left">
4
+ <%= format_date session.created_at %>
5
+ </div>
6
+ <div class="media-content">
7
+ <div class="content">
8
+ <p>
9
+ <strong class="session__reason"><%= session.reason %></strong>
10
+ <%= sensitive_session_decoration(session) %>
11
+ <br>
12
+ by <%= session.user.username %>
13
+ </p>
14
+ </div>
15
+ </div>
16
+
17
+ <div class="session__status media-right">
18
+ <% if session.audits.any? %>
19
+ <%= session.audits.pluck(:status).collect(&:humanize).join(", ") %>
20
+ <% else %>
21
+ Pending
22
+ <% end %>
23
+ </div>
24
+ </article>
25
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <article class="message is-info">
2
+ <div class="message-body">
3
+ <%= sessions.count %> sessions (<%= sessions.pending.count %> pending, <%= sessions.approved.count %> approved, <%= sessions.flagged.count %> flagged)
4
+ </div>
5
+ </article>
@@ -0,0 +1,6 @@
1
+ <%= render "audits1984/sessions/filter", filtered_sessions: @filtered_sessions %>
2
+ <%= render "audits1984/sessions/summary", sessions: @sessions %>
3
+
4
+ <div class="sessions">
5
+ <%= render partial: "audits1984/sessions/session", collection: @sessions, cached: true %>
6
+ </div>
@@ -0,0 +1,10 @@
1
+ <nav class="breadcrumb">
2
+ <ul>
3
+ <li><%= link_to "Sessions", sessions_path %></li>
4
+ <li class="is-active"><a href="#" aria-current="page">Session <%= @session.id %> by <%= @session.user.username %></a></li>
5
+ </ul>
6
+ </nav>
7
+
8
+ <%= render "audits1984/sessions/header", session: @session, audit: @audit %>
9
+ <%= render "audits1984/sessions/executed_code", session: @session %>
10
+ <%= render "audits1984/sessions/audit_form", session: @session, audit: @audit %>
@@ -0,0 +1,10 @@
1
+ <% flash.each do |name, message| -%>
2
+ <% message_class = { notice: "is-success", alert: "is-danger" }[name.to_sym] %>
3
+
4
+ <article class="message <%= message_class %>">
5
+ <div class="message-body">
6
+ <%= message %>
7
+ </div>
8
+ </article>
9
+
10
+ <% end -%>
@@ -0,0 +1,24 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title><%= @title || "Audits1984" %></title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+
8
+ <%= csrf_meta_tags %>
9
+
10
+ <%= stylesheet_link_tag "audits1984/bulma.min" %>
11
+ <%= javascript_importmap_tags "application", importmap: Audits1984.importmap %>
12
+ <%= stylesheet_link_tag "audits1984/application", media: :all, data: { turbo_track: :reload } %>
13
+ </head>
14
+
15
+ <body>
16
+
17
+ <section class="section">
18
+ <div class="container">
19
+ <%= render "layouts/audits1984/flash" %>
20
+ <%= yield %>
21
+ </div>
22
+ </section>
23
+ </body>
24
+ </html>
@@ -0,0 +1,2 @@
1
+ pin "application", to: "audits1984/application.js", preload: true
2
+ pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
data/config/routes.rb ADDED
@@ -0,0 +1,9 @@
1
+ Audits1984::Engine.routes.draw do
2
+ resources :sessions, only: %i[ index show ] do
3
+ resources :audits, only: %i[ create update ]
4
+ end
5
+
6
+ resource :filtered_sessions, only: %i[ update ]
7
+
8
+ root to: "sessions#index"
9
+ end
@@ -0,0 +1,12 @@
1
+ class CreateAuditingTables < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :audits1984_audits do |t|
4
+ t.integer :status, default: 0, null: false
5
+ t.text :notes
6
+ t.references :session, null: false
7
+ t.references :auditor, null: false
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,46 @@
1
+ require "console1984"
2
+ require "importmap-rails"
3
+ require "turbo-rails"
4
+ require "rinku"
5
+
6
+ module Audits1984
7
+ class Engine < ::Rails::Engine
8
+ isolate_namespace Audits1984
9
+
10
+ initializer "audits1984.middleware" do |app|
11
+ if app.config.api_only
12
+ app.middleware.use ActionDispatch::Flash
13
+ app.middleware.use ::Rack::MethodOverride
14
+ end
15
+ end
16
+
17
+ config.audits1984 = ActiveSupport::OrderedOptions.new
18
+
19
+ initializer "audits1984.config" do
20
+ config.audits1984.each do |key, value|
21
+ Audits1984.send("#{key}=", value)
22
+ end
23
+ end
24
+
25
+ initializer "audits1984.session" do
26
+ ActiveSupport.on_load(:console_1984_session) do
27
+ include Audits1984::Session::Auditable, Audits1984::Session::Iterable
28
+ end
29
+ end
30
+
31
+ initializer "audits1984.assets" do |app|
32
+ app.config.assets.paths << root.join("app/assets/stylesheets")
33
+ app.config.assets.paths << root.join("app/javascript")
34
+ app.config.assets.precompile << "audits1984_manifest.js"
35
+ end
36
+
37
+ initializer "audits1984.importmap", after: "importmap" do |app|
38
+ Audits1984.importmap.draw(root.join("config/importmap.rb"))
39
+ Audits1984.importmap.cache_sweeper(watches: root.join("app/javascript"))
40
+
41
+ ActiveSupport.on_load(:action_controller_base) do
42
+ before_action { Audits1984.importmap.cache_sweeper.execute_if_updated }
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,3 @@
1
+ module Audits1984
2
+ VERSION = '0.1.0'
3
+ end
data/lib/audits1984.rb ADDED
@@ -0,0 +1,13 @@
1
+ require "audits1984/version"
2
+ require "audits1984/engine"
3
+
4
+ require "zeitwerk"
5
+ loader = Zeitwerk::Loader.for_gem
6
+ loader.setup
7
+
8
+ module Audits1984
9
+ mattr_accessor :auditor_class, default: "::User"
10
+ mattr_accessor :auditor_name_attribute, default: :name
11
+ mattr_accessor :base_controller_class, default: "::ApplicationController"
12
+ mattr_accessor :importmap, default: Importmap::Map.new
13
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :audits1984 do
3
+ # # Task goes here
4
+ # end