diff --git a/app.rb b/app.rb index 8f51a7c5..d3a71f70 100644 --- a/app.rb +++ b/app.rb @@ -22,6 +22,10 @@ helpers do %{} end + def require_admin + redirect '/' unless signed_in? && current_site.is_admin + end + def hcaptcha_input %{ @@ -77,6 +81,8 @@ before do content_type :json elsif request.path.match /^\/webhooks\// # Skips the CSRF/validation check for stripe web hooks + elsif request.path.match /^\/admin/ + require_admin elsif current_site && current_site.email_not_validated? && !(request.path =~ /^\/site\/.+\/confirm_email|^\/settings\/change_email|^\/welcome|^\/supporter|^\/signout/) redirect "/site/#{current_site.username}/confirm_email" elsif current_site && current_site.phone_verification_needed? && !(request.path =~ /^\/site\/.+\/confirm_email|^\/settings\/change_email|^\/site\/.+\/confirm_phone|^\/welcome|^\/supporter|^\/signout/) diff --git a/app/admin.rb b/app/admin.rb index acecd12f..9ffbb1e0 100644 --- a/app/admin.rb +++ b/app/admin.rb @@ -1,18 +1,15 @@ get '/admin' do - require_admin @banned_sites = Site.select(:username).filter(is_banned: true).order(:username).all @nsfw_sites = Site.select(:username).filter(is_nsfw: true).order(:username).all erb :'admin' end get '/admin/reports' do - require_admin @reports = Report.order(:created_at.desc).all erb :'admin/reports' end get '/admin/site/:username' do |username| - require_admin @site = Site[username: username] not_found if @site.nil? @title = "Site Inspector - #{@site.username}" @@ -20,11 +17,9 @@ get '/admin/site/:username' do |username| end post '/admin/reports' do - end post '/admin/site_files/train' do - require_admin site = Site[params[:site_id]] site_file = site.site_files_dataset.where(path: params[:path]).first not_found if site_file.nil? @@ -34,7 +29,6 @@ post '/admin/site_files/train' do end get '/admin/usage' do - require_admin today = Date.today current_month = Date.new today.year, today.month, 1 @@ -69,12 +63,10 @@ get '/admin/usage' do end get '/admin/email' do - require_admin erb :'admin/email' end get '/admin/stats' do - require_admin @stats = { total_hosted_site_hits: DB['SELECT SUM(hits) FROM sites'].first[:sum], @@ -171,8 +163,6 @@ get '/admin/stats' do end post '/admin/email' do - require_admin - %i{subject body}.each do |k| if params[k].nil? || params[k].empty? flash[:error] = "#{k.capitalize} is missing." @@ -209,9 +199,7 @@ post '/admin/email' do redirect '/' end -post '/admin/banhammer' do - require_admin - +post '/admin/ban' do if params[:usernames].empty? flash[:error] = 'no usernames provided' redirect '/admin' @@ -259,8 +247,26 @@ post '/admin/banhammer' do redirect '/admin' end +post '/admin/unban' do + site = Site[username: params[:username]] + + if site.nil? + flash[:error] = 'User not found' + redirect '/admin' + end + + if !site.is_banned + flash[:error] = 'Site is not banned' + redirect '/admin' + end + + site.unban! + + flash[:success] = "Site #{site.username} was unbanned." + redirect '/admin' +end + post '/admin/mark_nsfw' do - require_admin site = Site[username: params[:username]] if site.nil? @@ -277,7 +283,6 @@ post '/admin/mark_nsfw' do end post '/admin/feature' do - require_admin site = Site[username: params[:username]] if site.nil? @@ -292,17 +297,9 @@ post '/admin/feature' do end get '/admin/masquerade/:username' do - require_admin site = Site[username: params[:username]] not_found if site.nil? session[:id] = site.id redirect '/' end -def require_admin - redirect '/' unless is_admin? -end - -def is_admin? - signed_in? && current_site.is_admin -end \ No newline at end of file diff --git a/tests/acceptance/admin_tests.rb b/tests/acceptance/admin_tests.rb index 2d01f923..d1ba96e9 100644 --- a/tests/acceptance/admin_tests.rb +++ b/tests/acceptance/admin_tests.rb @@ -4,11 +4,9 @@ describe '/admin' do include Capybara::DSL include Capybara::Minitest::Assertions - before do Capybara.reset_sessions! @admin = Fabricate :site, is_admin: true - @site = Fabricate :site page.set_rack_session id: @admin.id visit '/admin' end @@ -17,13 +15,42 @@ describe '/admin' do include Capybara::DSL it 'works for admin site' do - _(page.body).must_match /Administration/ + _(page.body).must_match /Admin/ end - it 'fails for site without admin' do - page.set_rack_session id: @site.id + it 'blocks all /admin paths for non-admin users' do + non_admin_site = Fabricate :site + page.set_rack_session id: non_admin_site.id + + # Test GET routes + ['/admin', '/admin/reports', '/admin/usage', '/admin/email', '/admin/stats', '/admin/masquerade/test'].each do |path| + visit path + _(page.current_path).must_equal '/', "Failed to block GET #{path}" + end + + # Test POST routes + ['/admin/reports', '/admin/ban', '/admin/unban', '/admin/mark_nsfw', '/admin/feature', '/admin/email'].each do |path| + page.driver.post path, {} + _(page.driver.status_code).must_equal 302, "Expected redirect for POST #{path}" + + # Follow the redirect and verify we end up at home page (blocked) + visit page.driver.response_headers['Location'] if page.driver.response_headers['Location'] + _(page.current_path).must_equal '/', "POST #{path} should redirect to home page, not #{page.current_path}" + end + end + + it 'blocks /admin paths for signed out users' do + page.set_rack_session id: nil + visit '/admin' _(page.current_path).must_equal '/' + + page.driver.post '/admin/ban', {usernames: 'test'} + _(page.driver.status_code).must_equal 302 + + # Follow the redirect and verify we end up at home page (blocked) + visit page.driver.response_headers['Location'] if page.driver.response_headers['Location'] + _(page.current_path).must_equal '/', "Signed out user POST should redirect to home page" end end @@ -31,20 +58,147 @@ describe '/admin' do include Capybara::DSL it 'works for valid site' do + site = Fabricate :site + within(:css, '#upgradeToSupporter') do - fill_in 'username', with: @site.username + fill_in 'username', with: site.username click_button 'Upgrade to Supporter' - @site.reload - _(@site.stripe_customer_id).wont_be_nil - _(@site.stripe_subscription_id).wont_be_nil - _(@site.values[:plan_type]).must_equal 'special' - _(@site.supporter?).must_equal true + site.reload + _(site.stripe_customer_id).wont_be_nil + _(site.stripe_subscription_id).wont_be_nil + _(site.values[:plan_type]).must_equal 'special' + _(site.supporter?).must_equal true end end end + describe 'ban site form' do + include Capybara::DSL + it 'bans single site successfully' do + site_to_ban = Fabricate :site + + fill_in 'usernames', with: site_to_ban.username + # select 'Spam', from: 'classifier' + click_button 'Ban' + + site_to_ban.reload + _(site_to_ban.is_banned).must_equal true + _(page.body).must_match(/sites have been banned/) + end + + it 'bans multiple sites successfully' do + site1 = Fabricate :site + site2 = Fabricate :site + + fill_in 'usernames', with: "#{site1.username}\n#{site2.username}" + #select 'Phishing', from: 'classifier' + click_button 'Ban' + + site1.reload + site2.reload + _(site1.is_banned).must_equal true + _(site2.is_banned).must_equal true + _(page.body).must_match(/sites have been banned/) + end + + it 'bans sites using IP when checkbox is checked' do + ip_address = '192.168.1.1' + site1 = Fabricate :site, ip: ip_address + site2 = Fabricate :site, ip: ip_address + + fill_in 'usernames', with: site1.username + check 'ban_using_ips' + select 'Spam', from: 'classifier' + click_button 'Ban' + + site1.reload + site2.reload + _(site1.is_banned).must_equal true + _(site2.is_banned).must_equal true + end + end + + describe 'unban site form' do + include Capybara::DSL + + before do + @banned_site = Fabricate :site, is_banned: true + end + + it 'unbans site successfully' do + within(:css, 'form[action="/admin/unban"]') do + fill_in 'username', with: @banned_site.username + click_button 'Unban' + end + + @banned_site.reload + _(@banned_site.is_banned).must_equal false + _(page.body).must_match(/was unbanned/) + end + + it 'handles non-existent username gracefully' do + within(:css, 'form[action="/admin/unban"]') do + fill_in 'username', with: 'nonexistent_user' + click_button 'Unban' + end + + _(page.body).must_match(/User not found/) + end + end + + describe 'mark as NSFW form' do + include Capybara::DSL + + it 'marks site as NSFW successfully' do + site_to_mark = Fabricate :site + + within(:css, 'form[action="/admin/mark_nsfw"]') do + fill_in 'username', with: site_to_mark.username + click_button 'Mark NSFW' + end + + site_to_mark.reload + _(site_to_mark.is_nsfw).must_equal true + _(page.body).must_match(/MISSION ACCOMPLISHED/) + end + + it 'handles non-existent username gracefully' do + within(:css, 'form[action="/admin/mark_nsfw"]') do + fill_in 'username', with: 'nonexistent_user' + click_button 'Mark NSFW' + end + + _(page.body).must_match(/User not found/) + end + end + + describe 'feature site form' do + include Capybara::DSL + + it 'features site successfully' do + site_to_feature = Fabricate :site + + within(:css, '#featureSite') do + fill_in 'username', with: site_to_feature.username + click_button 'Feature Site' + end + + site_to_feature.reload + _(site_to_feature.featured_at).wont_be_nil + _(page.body).must_match(/Site has been featured/) + end + + it 'handles non-existent username gracefully' do + within(:css, '#featureSite') do + fill_in 'username', with: 'nonexistent_user' + click_button 'Feature Site' + end + + _(page.body).must_match(/User not found/) + end + end describe 'email blasting' do before do diff --git a/views/admin.erb b/views/admin.erb index e8962cb0..06e9deaf 100644 --- a/views/admin.erb +++ b/views/admin.erb @@ -1,7 +1,6 @@
-

Administration

-

Freedom Ain't Free

+

Admin

@@ -19,9 +18,8 @@

Ban Site

-
+ <%== csrf_token_input_html %> -

Site Username(s):

+
+

Unban Site

+
+ <%== csrf_token_input_html %> +

+ +

+

+
+
+
+ +

Mark as NSFW

<%== csrf_token_input_html %> -

Site Name:

- +

@@ -53,9 +63,7 @@
<%== csrf_token_input_html %> -

This site will be upgraded to the supporter plan.

-

Site Name:

-

+

@@ -64,9 +72,7 @@

Feature Site

<%== csrf_token_input_html %> -

This site will be featured on the front page and in a special browse section.

-

Site Name:

-

+