Page Menu
Home
GRNET
Search
Configure Global Search
Log In
Files
F1969603
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sun, May 17, 10:30 AM
Size
15 KB
Mime Type
text/x-diff
Expires
Tue, May 19, 10:30 AM (3 h, 12 m)
Engine
blob
Format
Raw Data
Handle
385180
Attached To
rWEBDNS WebDNS (edet4)
View Options
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 748289c..954b3b3 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,62 +1,65 @@
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
attr_writer :breadcrumb
helper_method :admin?
def admin?
return false if params.key?('user')
return false if current_user.nil?
@admin_count ||= begin
current_user
.groups
.where(name: WebDNS.settings[:admin_group]).count
end
@admin_count != 0
end
def admin_only!
return if admin?
redirect_to root_path, alert: 'Admin only area!'
end
private
def group
@group ||= edit_group_scope.find(params[:group_id] || params[:id])
end
def domain
@domain ||= edit_domain_scope.find(params[:domain_id] || params[:id])
end
def record
@record ||= record_scope.find(params[:record_id] || params[:id])
end
def show_group_scope
@show_group_scope ||= current_user.groups
end
def edit_group_scope
@edit_group_scope ||= admin? ? Group.all : show_group_scope
end
def show_domain_scope
@show_domain_scope ||= Domain.where(group: show_group_scope)
end
def edit_domain_scope
@edit_domain_scope ||= admin? ? Domain.all : Domain.where(group: show_group_scope)
end
def record_scope
@record_scope ||= domain.records
end
+ def notification
+ Notification.instance
+ end
end
diff --git a/app/controllers/domains_controller.rb b/app/controllers/domains_controller.rb
index 160b07b..72f9c0b 100644
--- a/app/controllers/domains_controller.rb
+++ b/app/controllers/domains_controller.rb
@@ -1,67 +1,74 @@
class DomainsController < ApplicationController
before_action :authenticate_user!
before_action :domain, only: [:show, :edit, :update, :destroy]
before_action :group, only: [:show, :edit, :update, :destroy]
helper_method :edit_group_scope
# GET /domains
def index
@domains = show_domain_scope.all
end
# GET /domains/1
def show
@record = Record.new(domain_id: @domain.id)
end
# GET /domains/new
def new
@domain = Domain.new
end
# GET /domains/1/edit
def edit
end
# POST /domains
def create
@domain = Domain.new(domain_params)
if @domain.save
+ notify_domain(@domain, :create)
redirect_to @domain, notice: "#{@domain.name} was successfully created."
else
render :new
end
end
# PATCH/PUT /domains/1
def update
if @domain.update(domain_params)
+ notify_domain(@domain, :update)
redirect_to @domain, notice: "#{@domain.name} was successfully updated."
else
render :edit
end
end
# DELETE /domains/1
def destroy
@domain.destroy
+ notify_domain(@domain, :destroy)
redirect_to domains_url, notice: "#{@domain.name} was successfully destroyed."
end
private
def group
domain.group
end
def domain_params
params.require(:domain).tap { |d|
# Make sure group id is permitted (belongs to edit_group_scope)
d[:group_id] = edit_group_scope.find_by_id(d[:group_id]).try(:id)
}.permit(:name, :type, :master, :group_id)
end
+ def notify_domain(*args)
+ notification.notify_domain(current_user, *args)
+ end
+
end
diff --git a/app/controllers/records_controller.rb b/app/controllers/records_controller.rb
index 2090230..5123ddc 100644
--- a/app/controllers/records_controller.rb
+++ b/app/controllers/records_controller.rb
@@ -1,62 +1,69 @@
class RecordsController < ApplicationController
before_action :authenticate_user!
before_action :domain
before_action :record, only: [:edit, :update, :destroy]
# GET /records/new
def new
@record = domain.records.build
end
# GET /records/1/edit
def edit
end
# POST /records
def create
@record = domain.records.new(new_record_params)
if @record.save
+ notify_record(@record, :create)
redirect_to domain, notice: 'Record was successfully created.'
else
flash[:alert] = 'There were some errors creating the record!'
render :new
end
end
# PATCH/PUT /records/1
def update
if @record.update(edit_record_params)
+ notify_record(@record, :update)
redirect_to domain, notice: 'Record was successfully updated.'
else
render :edit
end
end
# DELETE /records/1
def destroy
@record.destroy
+ notify_record(@record, :destroy)
redirect_to domain, notice: 'Record was successfully destroyed.'
end
private
def edit_record_params
if @record.type == 'SOA'
permitted = [:contact, :serial, :refresh, :retry, :expire, :nx]
else
permitted = [:name, :content, :ttl, :prio, :disable]
end
params.require(:record).permit(*permitted).tap { |r|
r[:drop_privileges] = true if not admin?
}
end
def new_record_params
params.require(:record).permit(:name, :content, :ttl, :type, :prio).tap { |r|
r[:drop_privileges] = true if not admin?
}
end
+
+ def notify_record(*args)
+ notification.notify_record(current_user, *args)
+ end
end
diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb
new file mode 100644
index 0000000..c7cde23
--- /dev/null
+++ b/app/mailers/notification_mailer.rb
@@ -0,0 +1,29 @@
+class NotificationMailer < ActionMailer::Base
+ default from: WebDNS.settings[:mail_from]
+
+ PREFIXES = {
+ create: 'Created',
+ update: 'Modified',
+ destroy: 'Deleted',
+ }
+
+ def notify_record(record:, context:, user:, admin:, others:, changes:)
+ @record = record
+ @context = context
+ @user = user
+ @admin = admin
+ @changes = changes
+
+ mail(to: others, subject: "[webdns] [record] #{PREFIXES[context.to_sym]} #{record.to_short_dns}")
+ end
+
+ def notify_domain(domain:, context:, user:, admin:, others:, changes:)
+ @domain = domain
+ @context = context
+ @user = user
+ @admin = admin
+ @changes = changes
+
+ mail(to: others, subject: "[webdns] [domain] #{PREFIXES[context.to_sym]} #{domain.name}")
+ end
+end
diff --git a/app/views/notification_mailer/notify_domain.text.erb b/app/views/notification_mailer/notify_domain.text.erb
new file mode 100644
index 0000000..ab2b51d
--- /dev/null
+++ b/app/views/notification_mailer/notify_domain.text.erb
@@ -0,0 +1,16 @@
+Action: <%= @context %>
+By: <%= @user.email %> <%= '(admin)' if @admin %>
+
+Domain: <%= @domain.name %>
+Type: <%= @domain.type %>
+<% if @domain.slave? %>
+Master: <%= @domain.master %>
+<% end -%>
+
+<% if @context == :update -%>
+Changes:
+<% @changes.each do |key, change| -%>
+<%= key %> from <%= change.first || "(empty)" %>
+<%= key %> to <%= change.last %>
+<% end -%>
+<% end -%>
diff --git a/app/views/notification_mailer/notify_record.text.erb b/app/views/notification_mailer/notify_record.text.erb
new file mode 100644
index 0000000..db5e335
--- /dev/null
+++ b/app/views/notification_mailer/notify_record.text.erb
@@ -0,0 +1,15 @@
+Action: <%= @context %>
+By: <%= @user.email %> <%= '(admin)' if @admin %>
+
+Record: <%= @record.name %>
+Type: <%= @record.type %>
+Domain: <%= @record.domain.name %>
+State: <%= @record.to_dns %>
+
+<% if @context == :update -%>
+Changes:
+<% @changes.each do |key, change| -%>
+<%= key %> from <%= change.first %>
+<%= key %> to <%= change.last %>
+<% end -%>
+<% end -%>
diff --git a/config/initializers/notification.rb b/config/initializers/notification.rb
new file mode 100644
index 0000000..ef750d3
--- /dev/null
+++ b/config/initializers/notification.rb
@@ -0,0 +1 @@
+Notification.instance.hook
diff --git a/lib/notification.rb b/lib/notification.rb
new file mode 100644
index 0000000..ec8063c
--- /dev/null
+++ b/lib/notification.rb
@@ -0,0 +1,93 @@
+require 'singleton'
+
+class Notification
+ include Singleton
+
+ # Send out a notification about notable record changes
+ def notify_record(user, record, context)
+ ActiveSupport::Notifications.instrument(
+ 'webdns.record',
+ user: user,
+ context: context,
+ object: record)
+ end
+
+ # Send out a notification about notable domain changes
+ def notify_domain(user, domain, context)
+ ActiveSupport::Notifications.instrument(
+ 'webdns.domain',
+ user: user,
+ context: context,
+ object: domain)
+ end
+
+ # Subscribe to domain/record notifications
+ def hook
+ hook_record
+ hook_domain
+ end
+
+ private
+
+ def hook_record
+ ActiveSupport::Notifications
+ .subscribe 'webdns.record' do |_name, _started, _finished, _unique_id, data|
+ handle_record(data)
+ end
+ end
+
+ def hook_domain
+ ActiveSupport::Notifications
+ .subscribe 'webdns.domain' do |_name, _started, _finished, _unique_id, data|
+ handle_domain(data)
+ end
+ end
+
+ def handle_record(data) # rubocop:disable Metrics/MethodLength
+ record, context, user = data.values_at(:object, :context, :user)
+ domain = record.domain
+ changes = record.previous_changes
+
+ # Nobody is interested in those
+ changes.delete('updated_at')
+ changes.delete('created_at')
+ return if changes.empty?
+
+ others = domain.group.users.where.not(id: user.id).pluck(:email)
+ return if others.empty?
+
+ admin_action = !user.groups.exists?(domain.group_id)
+
+ NotificationMailer.notify_record(
+ record: record,
+ context: context,
+ user: user,
+ admin: admin_action,
+ others: others,
+ changes: changes
+ ).deliver
+ end
+
+ def handle_domain(data) # rubocop:disable Metrics/MethodLength
+ domain, context, user = data.values_at(:object, :context, :user)
+ changes = domain.previous_changes
+
+ # Nobody is interested in those
+ changes.delete('updated_at')
+ changes.delete('created_at')
+ return if changes.empty?
+
+ others = domain.group.users.where.not(id: user.id).pluck(:email)
+ return if others.empty?
+
+ admin_action = !user.groups.exists?(domain.group_id)
+ NotificationMailer.notify_domain(
+ domain: domain,
+ context: context,
+ user: user,
+ admin: admin_action,
+ others: others,
+ changes: changes
+ ).deliver
+ end
+end
diff --git a/test/mailers/notification_mailer_test.rb b/test/mailers/notification_mailer_test.rb
new file mode 100644
index 0000000..33a9d40
--- /dev/null
+++ b/test/mailers/notification_mailer_test.rb
@@ -0,0 +1,146 @@
+require 'test_helper'
+
+class NotificationMailerTest < ActionMailer::TestCase
+
+ class DomainNotificationMailerTest < ActionMailer::TestCase
+ def setup
+ @notification = Notification.instance
+
+ @group = create(:group_with_users)
+ @domain = create(:domain, group: @group)
+ @record = build(:a, name: 'a', domain: @domain)
+ end
+
+ test 'domain add' do
+ @record.save!
+
+ @notification.notify_domain(@group.users.first, @domain, :create)
+
+ assert_not ActionMailer::Base.deliveries.empty?
+ mail = ActionMailer::Base.deliveries.last
+
+ assert_equal [@group.users.last.email], mail.to
+ assert_includes mail.subject, 'Created'
+ assert_includes mail.body.to_s, "Domain: #{@domain.name}"
+ assert_includes mail.body.to_s, "By: #{@group.users.first.email}"
+ end
+
+ test 'domain edit' do
+ @record.save!
+
+ @domain.type = 'SLAVE'
+ @domain.master = '1.2.3.4'
+ @domain.save!
+
+ @notification.notify_domain(@group.users.first, @domain, :update)
+
+ assert_not ActionMailer::Base.deliveries.empty?
+ mail = ActionMailer::Base.deliveries.last
+
+ assert_equal [@group.users.last.email], mail.to
+ assert_includes mail.subject, 'Modified'
+ assert_includes mail.body.to_s, "Domain: #{@domain.name}"
+ assert_includes mail.body.to_s, "By: #{@group.users.first.email}"
+ assert_includes mail.body.to_s, 'type from NATIVE'
+ assert_includes mail.body.to_s, 'type to SLAVE'
+ assert_includes mail.body.to_s, 'master from (empty)'
+ assert_includes mail.body.to_s, 'master to 1.2.3.4'
+ end
+
+ test 'domain destroy' do
+ @record.save!
+
+ @domain.destroy!
+
+ @notification.notify_domain(@group.users.first, @domain, :destroy)
+
+ assert_not ActionMailer::Base.deliveries.empty?
+ mail = ActionMailer::Base.deliveries.last
+
+ assert_equal [@group.users.last.email], mail.to
+ assert_includes mail.subject, 'Deleted'
+ assert_includes mail.body.to_s, "Domain: #{@domain.name}"
+ assert_includes mail.body.to_s, "By: #{@group.users.first.email}"
+ end
+ end
+
+ class DomainNotificationMailerTest < ActionMailer::TestCase
+ test 'record add' do
+ @record.save!
+
+ @notification.notify_record(@group.users.first, @record, :create)
+
+ assert_not ActionMailer::Base.deliveries.empty?
+ mail = ActionMailer::Base.deliveries.last
+
+ assert_equal [@group.users.last.email], mail.to
+ assert_includes mail.subject, 'Created'
+ assert_includes mail.body.to_s, "Record: #{@record.name}"
+ assert_includes mail.body.to_s, "Domain: #{@domain.name}"
+ assert_includes mail.body.to_s, "State: #{@record.to_dns}"
+ assert_includes mail.body.to_s, "By: #{@group.users.first.email}"
+ end
+
+ test 'record edit' do
+ @record.save!
+
+ prev_content = @record.content
+ @record.content = '1.1.1.1'
+ @record.save!
+
+ @notification.notify_record(@group.users.first, @record, :update)
+
+ assert_not ActionMailer::Base.deliveries.empty?
+ mail = ActionMailer::Base.deliveries.last
+
+ assert_equal [@group.users.last.email], mail.to
+ assert_includes mail.subject, 'Modified'
+ assert_includes mail.body.to_s, "Record: #{@record.name}"
+ assert_includes mail.body.to_s, "Domain: #{@domain.name}"
+ assert_includes mail.body.to_s, "State: #{@record.to_dns}"
+ assert_includes mail.body.to_s, "By: #{@group.users.first.email}"
+ assert_includes mail.body.to_s, "content from #{prev_content}"
+ assert_includes mail.body.to_s, 'content to 1.1.1.1'
+ end
+
+ test 'soa edit' do
+ @record = @domain.soa
+
+ prev_content = @record.content
+ @record.nx = 10
+ @record.save!
+
+ @notification.notify_record(@group.users.first, @record, :update)
+
+ assert_not ActionMailer::Base.deliveries.empty?
+ mail = ActionMailer::Base.deliveries.last
+
+ assert_equal [@group.users.last.email], mail.to
+ assert_includes mail.subject, 'Modified'
+ assert_includes mail.body.to_s, "Record: #{@record.name}"
+ assert_includes mail.body.to_s, "Domain: #{@domain.name}"
+ assert_includes mail.body.to_s, "State: #{@record.to_dns}"
+ assert_includes mail.body.to_s, "By: #{@group.users.first.email}"
+ assert_includes mail.body.to_s, "content from #{prev_content}"
+ assert_includes mail.body.to_s, "content to #{@record.content}"
+ assert_includes mail.body.to_s, ' 10'
+ end
+
+ test 'record destroy' do
+ @record.save!
+
+ @record.destroy!
+
+ @notification.notify_record(@group.users.first, @record, :destroy)
+
+ assert_not ActionMailer::Base.deliveries.empty?
+ mail = ActionMailer::Base.deliveries.last
+
+ assert_equal [@group.users.last.email], mail.to
+ assert_includes mail.subject, 'Deleted'
+ assert_includes mail.body.to_s, "Record: #{@record.name}"
+ assert_includes mail.body.to_s, "Domain: #{@domain.name}"
+ assert_includes mail.body.to_s, "By: #{@group.users.first.email}"
+ end
+ end
+end
Event Timeline
Log In to Comment