diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 01aae98..365ef77 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,48 +1,55 @@ class UsersController < ApplicationController before_action :authenticate_user! - before_action :user, only: [:mute, :unmute, :token, :generate_token] + before_action :user, only: [:mute, :unmute, :mute_all, :token, :generate_token] # GET /users/1/token def token end # POST /users/1/generate_token def generate_token @user.token = SecureRandom.hex(10) @user.save! redirect_to token_user_path(@user) end # PUT /users/1/unsubscribe/2 def mute domain = show_domain_scope.find(params[:domain_id]) @user.subscriptions.find_or_create_by!(domain: domain) redirect_to domains_url, notice: "Successfully unsubscribed from #{domain.name} notifications!" end # PUT /users/1/subscribe/2 def unmute domain = show_domain_scope.find(params[:domain_id]) # Drop all opt-outs @user.subscriptions.where(domain: domain).delete_all redirect_to domains_url, notice: "Successfully subscribed to #{domain.name} notifications!" end + # PUT /users/1/domains/mute + def mute_all + @user.mute_all_domains + + redirect_to domains_url, notice: "Successfully unsubscribed from all domain notifications!" + end + private def user @user ||= User.find(params[:user_id] || params[:id]) # Guard access to other user tokens if current_user.id != @user.id && !admin? redirect_to(root_path, alert: 'You need admin rights for that!') end @user end end diff --git a/app/models/user.rb b/app/models/user.rb index 51e2f75..66f7508 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,32 +1,48 @@ class User < ActiveRecord::Base devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable has_many :memberships has_many :groups, through: :memberships has_many :subscriptions, dependent: :delete_all scope :orphans, -> { includes(:memberships).where(:memberships => { user_id: nil }) } # Check if the user can change his password # # Remote users are not able to change their password def can_change_password? !identifier? end def to_api Hash[ :id, id, :email, email ].with_indifferent_access end def self.find_for_database_authentication(conditions) # Override devise method for database auth # We only want to auth local user via the database. find_first_by_auth_conditions(conditions, identifier: '') end + + def mute_all_domains + ActiveRecord::Base.transaction do + domain_ids = Domain.where(group: groups).pluck(:id) + domain_ids.each { |did| + + sub = self.subscriptions.create(domain_id: did) + if !sub.valid? + # Allow only domain_id (uniqueness) errors + raise x.errors.full_messages.join(', ') if sub.errors.size > 1 + raise x.errors.full_messages.join(', ') if !sub.errors[:domain_id] + end + + } + end + end end diff --git a/app/views/shared/_nav.html.erb b/app/views/shared/_nav.html.erb index e05d35d..a6281ba 100644 --- a/app/views/shared/_nav.html.erb +++ b/app/views/shared/_nav.html.erb @@ -1,57 +1,59 @@ diff --git a/config/routes.rb b/config/routes.rb index 6c277dc..3dac3f1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,86 +1,87 @@ Rails.application.routes.draw do # Override devise user removal devise_scope :users do delete :users, to: redirect('/') end devise_for :users get '/auth/saml', to: 'auth#saml' root to: redirect('/domains') resources :users, only: [] do get :token, to: 'users#token', on: :member post :generate_token, to: 'users#generate_token', on: :member resources :domains, only: [] do put :mute, to: 'users#mute' put :unmute, to: 'users#unmute' + put :mute, to: 'users#mute_all', on: :collection end end resources :groups, only: [:show] do get :search_member, to: 'groups#search_member', on: :member post :members, to: 'groups#create_member', as: :create_member, on: :member delete 'member/:user_id', to: 'groups#destroy_member', as: :destroy_member, on: :member end resources :domains do get :edit_dnssec, to: 'domains#edit_dnssec', on: :member delete :full_destroy, to: 'domains#full_destroy', on: :member resources :records, except: [:index, :show] do # Reuse records#update instead of introducing new controller actions # # rubocop:disable Style/AlignHash put :disable, to: 'records#update', on: :member, defaults: { record: { disabled: true } } put :enable, to: 'records#update', on: :member, defaults: { record: { disabled: false } } put :editable, to: 'records#editable', on: :collection post :valid, to: 'records#valid', on: :collection post :bulk, to: 'records#bulk', on: :collection # rubocop:enable Style/AlignHash end end get '/records/search', to: 'records#search' # Admin namespace :admin do root to: redirect('/admin/groups') resources :groups, except: [:show] resources :jobs, only: [:index, :destroy] do put :done, to: 'jobs#update', on: :member, defaults: { job: { status: 1 } } put :pending, to: 'jobs#update', on: :member, defaults: { job: { status: 0 } } get '/type/:category', to: 'jobs#index', on: :collection, constraints: proc { |req| ['completed', 'pending'].include?(req.params[:category]) } end resources :users, only: [:destroy] do get :orphans, to: 'users#orphans', on: :collection put :update_groups, to: 'users#update_groups', on: :collection end end # API scope '/api' do get :ping, to: 'api#ping' get :whoami, to: 'api#whoami' get '/domain/:domain/list', to: 'api#list', constraints: { domain: /[^\/]+/} post '/domain/:domain/bulk', to: 'api#bulk', constraints: { domain: /[^\/]+/} get :domains, to: 'api#domains' end if WebDNS.settings[:api] # Private put 'private/replace_ds', to: 'private#replace_ds' put 'private/trigger_event', to: 'private#trigger_event' get 'private/zones', to: 'private#zones' get 'help/api', to: 'help#api' end diff --git a/test/models/subscription_test.rb b/test/models/subscription_test.rb index b3f9a84..74018eb 100644 --- a/test/models/subscription_test.rb +++ b/test/models/subscription_test.rb @@ -1,17 +1,35 @@ require 'test_helper' class SubscriptionTest < ActiveSupport::TestCase test 'single subscription for a domain' do domain = create(:domain_with_subscriptions) assert_equal 1, domain.opt_outs.count subscription = domain.opt_outs.first assert_equal true, subscription.disabled user = subscription.user user.reload assert_equal domain, user.subscriptions.first.domain end + + test 'mute all domains for a user' do + d1 = create(:domain_with_subscriptions) + d2 = create(:domain_with_subscriptions) + user = create(:user) + + # Add user to the groups + d1.group.users << user + d2.group.users << user + + # Opt out from notifications + user.mute_all_domains + # Ensure this is re-entrant + user.mute_all_domains + + # Assert 2 opt-out domains + assert_equal 2, user.subscriptions.count + end end