Audit logging improved. References users, screenshots, packages

This commit is contained in:
Christoph Haas 2021-03-03 23:21:24 +01:00
parent b6dee6b93c
commit 2fef9c7043
12 changed files with 95 additions and 45 deletions

View file

@ -28,4 +28,10 @@ class ApplicationController < ActionController::Base
def after_sign_out_path_for(resource_or_scope) def after_sign_out_path_for(resource_or_scope)
root_path root_path
end end
# Write a message into the audit log in the database
def auditlog(message, level: 'info', screenshot: nil, package: nil)
Log.log(message, ip: request.remote_ip, user: current_user,
screenshot: screenshot, package: package)
end
end end

View file

@ -56,7 +56,6 @@ class PackagesController < ApplicationController
files=[files] if files.class != Array files=[files] if files.class != Array
files.each do |img| files.each do |img|
# Log.log "Uploaded img=#{img.path}"
new_screenshot = @package.screenshots.new(simage: img) new_screenshot = @package.screenshots.new(simage: img)
# Check if the image was valid # Check if the image was valid
@ -70,7 +69,8 @@ class PackagesController < ApplicationController
# Check that this screenshot is not a duplicate for this package # Check that this screenshot is not a duplicate for this package
if @package.screenshots.where(image_fingerprint: new_screenshot.image_fingerprint).any? if @package.screenshots.where(image_fingerprint: new_screenshot.image_fingerprint).any?
Log.log "Duplicate image with fingerprint #{new_screenshot.image_fingerprint} found. Rejecting." auditlog "Duplicate image with fingerprint #{new_screenshot.image_fingerprint} found. Rejecting.",
package: @package
all_errors << "Your file #{img.original_filename} is a duplicate. Sorry." all_errors << "Your file #{img.original_filename} is a duplicate. Sorry."
else else
# TODO: Can the upload get approved automatically? # TODO: Can the upload get approved automatically?
@ -79,28 +79,26 @@ class PackagesController < ApplicationController
# The user won't know that an account is created. # The user won't know that an account is created.
# But this makes it easier to track who screenshots belong to. # But this makes it easier to track who screenshots belong to.
unless user_signed_in? unless user_signed_in?
sign_in User.create_pseudo_user sign_in User.create_pseudo_user
auditlog "New pseudo user for anonymous upload created and logged in.",
package: @package, screenshot: new_screenshot
end end
new_screenshot.user = current_user new_screenshot.user = current_user
new_screenshot.approve! if can?(:approve, new_screenshot) new_screenshot.approve! if can?(:approve, new_screenshot)
# Pre-render screenshot in different sizes # Pre-render screenshot in different sizes
new_screenshot.simage_derivatives! new_screenshot.simage_derivatives!
Log.log "Derivatives created"
new_screenshot.save! new_screenshot.save!
Log.log "Screenshot #{new_screenshot.id} uploaded successfully. " + \ auditlog "Screenshot uploaded successfully.",
"ip=#{new_screenshot.uploaderip}. "+ \ screenshot: new_screenshot, package: @package
#"user-hash=#{new_screenshot.uploaderhash}. "+ \
"user-name=#{current_user} "+ \
"image-fingerprint=#{new_screenshot.image_fingerprint} "+ \
"image-path=#{img.path}"
@valid_images.push new_screenshot @valid_images.push new_screenshot
end end
else else
errors = new_screenshot.errors[:simage] errors = new_screenshot.errors[:simage]
Log.log "Screenshot #{img.original_filename} invalid (#{errors})." auditlog "Screenshot #{img.original_filename} invalid (#{errors}).",
package: @package
#@invalid_images.push img.original_filename #@invalid_images.push img.original_filename
#raise #raise
all_errors << "Your file #{img.original_filename} #{errors.join(' and ')}." all_errors << "Your file #{img.original_filename} #{errors.join(' and ')}."
@ -113,7 +111,7 @@ class PackagesController < ApplicationController
# # Redirect back to upload form if all uploads were invalid # # Redirect back to upload form if all uploads were invalid
# unless @valid_images.any? # unless @valid_images.any?
# Log.log "No valid images uploaded. Back to upload form." # auditlog "No valid images uploaded. Back to upload form."
# redirect_to(upload_path, error: all_errors) and return # redirect_to(upload_path, error: all_errors) and return
# end # end
@ -162,10 +160,12 @@ class PackagesController < ApplicationController
new_screenshot.uploaderip = session[:ip] new_screenshot.uploaderip = session[:ip]
new_screenshot.version = @package.version new_screenshot.version = @package.version
new_screenshot.save new_screenshot.save
Log.log "Screenshot #{new_screenshot.id} uploaded successfully from legacy upload form." auditlog "Screenshot #{new_screenshot.id} uploaded successfully from legacy upload form.",
screenshot: new_screenshot, package: @package
redirect_to package_path(params[:packagename]) redirect_to package_path(params[:packagename])
else else
Log.log "Screenshot upload rejected. Errors: #{new_screenshot.errors.to_a}" auditlog "Screenshot upload rejected. Errors: #{new_screenshot.errors.to_a}",
package: @package
head :not_acceptable head :not_acceptable
end end
end end
@ -177,7 +177,8 @@ class PackagesController < ApplicationController
# Check if the user is allowed to change this screenshot # Check if the user is allowed to change this screenshot
# - Is this the user's own screenshot? (anonymous) # - Is this the user's own screenshot? (anonymous)
if (@screenshot.user == current_user) || (can? :destroy, @screenshot) if (@screenshot.user == current_user) || (can? :destroy, @screenshot)
logger.debug "User #{current_user} deletes screenshot #{@screenshot}" auditlog "Screenshot #{@screenshot} deleted",
package: @screenshot.package
@screenshot.destroy @screenshot.destroy
flash['notice'] = "Screenshot deleted." flash['notice'] = "Screenshot deleted."
redirect_back(fallback_location: package_path(name: @screenshot.package.name)) redirect_back(fallback_location: package_path(name: @screenshot.package.name))
@ -191,6 +192,8 @@ class PackagesController < ApplicationController
if can? :approve, @screenshot if can? :approve, @screenshot
@screenshot.approve! @screenshot.approve!
auditlog "Screenshot approved",
package: @screenshot.package, screenshot: @screenshot
flash['notice'] = "Screenshot approved." flash['notice'] = "Screenshot approved."
redirect_back(fallback_location: package_path(name: @screenshot.package.name)) redirect_back(fallback_location: package_path(name: @screenshot.package.name))
else else

View file

@ -3,19 +3,6 @@ class Users::MySessionsController < Devise::SessionsController
# Hook after successful login # Hook after successful login
def after_login def after_login
Log.log "User #{current_user.email} logged in.", request.remote_ip auditlog "User logged in."
old_screenshots = session[:uploaded_screenshots]
if old_screenshots.to_a.length > 0
old_screenshots.each do |id|
next unless id # sometimes we had 'nil' here
ss = Screenshot.find(id)
ss.user = @user
ss.save!
end
# The message is probably confusing. Just add the screenshots.
# flash[:info] = "#{old_screenshots.to_a.length} uploads have been added to your account"
Log.log "Anonymously uploaded screenshots #{old_screenshots} added to #{current_user.email}.", request.remote_ip
session[:uploaded_screenshots] = nil
end
end end
end end

View file

@ -1,6 +1,9 @@
# require 'pry' # require 'pry'
class Log < ApplicationRecord class Log < ApplicationRecord
belongs_to :user
belongs_to :package
belongs_to :screenshot
validates :message, presence: true validates :message, presence: true
validates :section, presence: true validates :section, presence: true
@ -13,7 +16,7 @@ class Log < ApplicationRecord
order('id DESC') order('id DESC')
} }
def self.log(message, ip_address='0.0.0.0', section='frontend', level='info') def self.log(message, ip: nil, user: nil, section: 'frontend', level: 'info', screenshot: nil, package: nil)
log = Log.new log = Log.new
ip_address = request.remote_ip if defined? request ip_address = request.remote_ip if defined? request
@ -22,8 +25,10 @@ class Log < ApplicationRecord
log.message = message log.message = message
log.section = section log.section = section
log.level = level log.level = level
log.ip_address = ip_address log.ip_address = ip.to_s
log.token = token log.screenshot = screenshot
log.package = package
log.user = user # current_user
logger.error "Could not log to database - validation errors: #{log.errors.to_a}" unless log.valid? logger.error "Could not log to database - validation errors: #{log.errors.to_a}" unless log.valid?
log.save log.save
# log # log

View file

@ -1,5 +1,5 @@
class User < ApplicationRecord class User < ApplicationRecord
has_many :screenshots, :inverse_of=>:user has_many :screenshots, inverse_of: :user
# Include default devise modules. Others available are: # Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable # :confirmable, :lockable, :timeoutable and :omniauthable
@ -78,7 +78,14 @@ class User < ApplicationRecord
password: generated_password, password: generated_password,
pseudo: true pseudo: true
) )
Log.log "New pseudo user for anonymous upload created: #{new_user}"
return new_user return new_user
end end
def to_s
if self.pseudo
"Pseudo user"
else
"#{self.email} (#{self.provider})"
end
end
end end

View file

@ -14,16 +14,34 @@
table table
thead thead
tr tr
th Message th Message
th Level th Level
th Section th Section
th IP th Package
th Screenshot
th User
th IP
tbody tbody
- @logs.each do |log| - @logs.each do |log|
tr tr
td #{log.message } td #{log.message}
td #{log.level } td #{log.level}
td #{log.section } td #{log.section}
td
- if log.package
= log.package.name
- else
' -
td
- if log.screenshot
= log.screenshot.id
- else
' -
td
- if log.user
= log.user.to_s
- else
' -
td #{log.ip_address} td #{log.ip_address}
- else - else

View file

@ -0,0 +1,5 @@
class AddUserFieldToLog < ActiveRecord::Migration[6.1]
def change
add_column :logs, :user, :string, default: nil
end
end

View file

@ -0,0 +1,5 @@
class RemoveTokenFieldFromLog < ActiveRecord::Migration[6.1]
def change
remove_column :logs, :token
end
end

View file

@ -0,0 +1,6 @@
class ChangeTypeOfUserFieldInLogs < ActiveRecord::Migration[6.1]
def change
remove_column :logs, :user
add_column :logs, :user_id, :integer
end
end

View file

@ -0,0 +1,6 @@
class AddScreenshotAndPackageFieldToLogs < ActiveRecord::Migration[6.1]
def change
add_column :logs, :package_id, :integer
add_column :logs, :screenshot_id, :integer
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_02_28_191717) do ActiveRecord::Schema.define(version: 2021_03_03_213303) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -59,10 +59,12 @@ ActiveRecord::Schema.define(version: 2021_02_28_191717) do
t.string "message" t.string "message"
t.string "level" t.string "level"
t.string "section" t.string "section"
t.string "token"
t.inet "ip_address" t.inet "ip_address"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.integer "user_id"
t.integer "package_id"
t.integer "screenshot_id"
end end
create_table "packages", id: :serial, force: :cascade do |t| create_table "packages", id: :serial, force: :cascade do |t|

View file

@ -53,9 +53,9 @@ How Debian repositories are organized: https://wiki.debian.org/RepositoryFormat
Logging from within the application into the database's "log" table: Logging from within the application into the database's "log" table:
Log.log "foo" auditlog "foo"
or with section (default='frontend'): or with section (default='frontend'):
Log.log "foo", "importer" Log.log "foo", nil, nil, "importer"
--- ---