From 98242dc68b1fb8b01d126a5c6b153dd2dea56a6d Mon Sep 17 00:00:00 2001 From: benhalpern Date: Thu, 16 Jul 2020 06:59:20 -0400 Subject: [PATCH 1/4] Initial work --- app/controllers/application_controller.rb | 8 ++++++++ app/models/site_config.rb | 1 + 2 files changed, 9 insertions(+) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index eca905460..b953a2613 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,6 @@ class ApplicationController < ActionController::Base skip_before_action :track_ahoy_visit + before_action :verify_private_forem protect_from_forgery with: :exception, prepend: true include SessionCurrentUser @@ -15,6 +16,13 @@ class ApplicationController < ActionController::Base error_too_many_requests(exc) end + def verify_private_forem + if SiteConfig.access == "private" + render template: "devise/registrations/new" + return + end + end + def not_found raise ActiveRecord::RecordNotFound, "Not Found" end diff --git a/app/models/site_config.rb b/app/models/site_config.rb index 280c8fcea..9523347c8 100644 --- a/app/models/site_config.rb +++ b/app/models/site_config.rb @@ -132,6 +132,7 @@ class SiteConfig < RailsSettings::Base # These are the default UX settings, which can be overridded by individual user preferences. # basic (current default), rich (cover image on all posts), compact (more minimal) field :feed_style, type: :string, default: "basic" + field :access, type: :string, default: "public" # Broadcast field :welcome_notifications_live_at, type: :date From b611a9847a2fe9d1e2dee2558c4192ec98866e0e Mon Sep 17 00:00:00 2001 From: benhalpern Date: Thu, 16 Jul 2020 09:58:00 -0400 Subject: [PATCH 2/4] Enable public or private forem config --- app/controllers/application_controller.rb | 12 ++++++++-- .../internal/configs_controller.rb | 1 + app/models/site_config.rb | 5 +++- app/views/internal/configs/show.html.erb | 5 ++++ spec/requests/api/v0/users_spec.rb | 24 +++++++++++++++++++ spec/requests/async_info_spec.rb | 6 +++++ spec/requests/ga_events_spec.rb | 8 +++++++ spec/requests/internal/configs_spec.rb | 14 +++++++++++ spec/requests/shells_spec.rb | 13 ++++++++++ spec/requests/stories_index_spec.rb | 12 ++++++++++ 10 files changed, 97 insertions(+), 3 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b953a2613..af9863b7b 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -17,9 +17,13 @@ class ApplicationController < ActionController::Base end def verify_private_forem - if SiteConfig.access == "private" + return if %w[shell async_info ga_events].include?(controller_name) + return if user_signed_in? || SiteConfig.public + + if api_action? + authenticate! + else render template: "devise/registrations/new" - return end end @@ -115,4 +119,8 @@ class ApplicationController < ActionController::Base def anonymous_user User.new(ip_address: request.env["HTTP_FASTLY_CLIENT_IP"]) end + + def api_action? + self.class.to_s.start_with?("Api::") + end end diff --git a/app/controllers/internal/configs_controller.rb b/app/controllers/internal/configs_controller.rb index 747185684..a68ce94ef 100644 --- a/app/controllers/internal/configs_controller.rb +++ b/app/controllers/internal/configs_controller.rb @@ -35,6 +35,7 @@ module Internal health_check_token feed_style sponsor_headline + public ] allowed_params = allowed_params | diff --git a/app/models/site_config.rb b/app/models/site_config.rb index 9523347c8..1e1278758 100644 --- a/app/models/site_config.rb +++ b/app/models/site_config.rb @@ -132,7 +132,10 @@ class SiteConfig < RailsSettings::Base # These are the default UX settings, which can be overridded by individual user preferences. # basic (current default), rich (cover image on all posts), compact (more minimal) field :feed_style, type: :string, default: "basic" - field :access, type: :string, default: "public" + # a non-public forem will redirect all unauthenticated pages to the registration page. + # a public forem could have more fine-grained authentication (listings ar private etc.) in future + field :public, type: :boolean, default: 1 + # Broadcast field :welcome_notifications_live_at, type: :date diff --git a/app/views/internal/configs/show.html.erb b/app/views/internal/configs/show.html.erb index 5ed5d6875..097144ef8 100644 --- a/app/views/internal/configs/show.html.erb +++ b/app/views/internal/configs/show.html.erb @@ -736,6 +736,11 @@ placeholder: "basic, rich, or compact" %>
Determines which default feed the users sees (rich content, more minimal, etc.)
+
+ <%= internal_config_label :public %> + <%= f.check_box :public, checked: SiteConfig.public %> +
Are most of the site pages (posts, profiles, etc. public?) — BE VERY CAUTIOUS IN CHANGING THIS
+
diff --git a/spec/requests/api/v0/users_spec.rb b/spec/requests/api/v0/users_spec.rb index 5fa2a383b..7a94ba3b0 100644 --- a/spec/requests/api/v0/users_spec.rb +++ b/spec/requests/api/v0/users_spec.rb @@ -26,6 +26,12 @@ RSpec.describe "Api::V0::Users", type: :request do expect(response).to have_http_status(:ok) end + it "returns unauthenticated if no authentication and site config is set to private" do + SiteConfig.public = false + get api_user_path("by_username"), params: { url: user.username } + expect(response).to have_http_status(:unauthorized) + end + it "returns the correct json representation of the user", :aggregate_failures do get api_user_path(user.id) @@ -70,6 +76,24 @@ RSpec.describe "Api::V0::Users", type: :request do expect(response_user["joined_at"]).to eq(user.created_at.strftime("%b %e, %Y")) expect(response_user["profile_image"]).to eq(ProfileImage.new(user).get(width: 320)) end + + it "returns 200 if no authentication and site config is set to private but user is authenticated" do + SiteConfig.public = false + get me_api_users_path, params: { access_token: access_token.token } + + response_user = response.parsed_body + + expect(response_user["type_of"]).to eq("user") + + %w[ + id username name summary twitter_username github_username website_url location + ].each do |attr| + expect(response_user[attr]).to eq(user.public_send(attr)) + end + + expect(response_user["joined_at"]).to eq(user.created_at.strftime("%b %e, %Y")) + expect(response_user["profile_image"]).to eq(ProfileImage.new(user).get(width: 320)) + end end end end diff --git a/spec/requests/async_info_spec.rb b/spec/requests/async_info_spec.rb index 179de25ba..a76154498 100644 --- a/spec/requests/async_info_spec.rb +++ b/spec/requests/async_info_spec.rb @@ -13,6 +13,12 @@ RSpec.describe "AsyncInfo", type: :request do get "/async_info/base_data" expect(response.parsed_body.keys).to match_array(%w[broadcast param token]) end + + it "renders normal response even if site config is private" do + SiteConfig.public = false + get "/async_info/base_data" + expect(response.parsed_body.keys).to match_array(%w[broadcast param token]) + end end context "when logged in" do diff --git a/spec/requests/ga_events_spec.rb b/spec/requests/ga_events_spec.rb index 1071ceab3..4f5d7a37c 100644 --- a/spec/requests/ga_events_spec.rb +++ b/spec/requests/ga_events_spec.rb @@ -13,5 +13,13 @@ RSpec.describe "GaEvents", type: :request, vcr: vcr_option do }.to_json expect(response.body).to eq("") end + + it "renders normal response even if site config is private" do + SiteConfig.public = false + post "/fallback_activity_recorder", params: { + path: "/ben", user_language: "en" + }.to_json + expect(response.body).to eq("") + end end end diff --git a/spec/requests/internal/configs_spec.rb b/spec/requests/internal/configs_spec.rb index 8077372c5..a87e53b71 100644 --- a/spec/requests/internal/configs_spec.rb +++ b/spec/requests/internal/configs_spec.rb @@ -498,6 +498,20 @@ RSpec.describe "/internal/config", type: :request do confirmation: confirmation_message } expect(SiteConfig.feed_style).to eq(feed_style) end + + it "updates public to true" do + is_private = true + post "/internal/config", params: { site_config: { private: is_private }, + confirmation: confirmation_message } + expect(SiteConfig.private).to eq(is_private) + end + + it "updates public to false" do + is_private = false + post "/internal/config", params: { site_config: { private: is_private }, + confirmation: confirmation_message } + expect(SiteConfig.private).to eq(is_private) + end end end end diff --git a/spec/requests/shells_spec.rb b/spec/requests/shells_spec.rb index 31f57b40c..d73abd602 100644 --- a/spec/requests/shells_spec.rb +++ b/spec/requests/shells_spec.rb @@ -11,6 +11,12 @@ RSpec.describe "Shells", type: :request do get "/shell_top" expect(response.header["Surrogate-Key"]).to include("shell-top") end + + it "renders normal response even if site config is private" do + SiteConfig.public = false + get "/shell_top" + expect(response.body).to include("user-signed-in") + end end describe "GET /shell_bottom" do @@ -23,5 +29,12 @@ RSpec.describe "Shells", type: :request do get "/shell_bottom" expect(response.header["Surrogate-Key"]).to include("shell-bottom") end + + it "renders normal response even if site config is private" do + SiteConfig.public = false + get "/shell_bottom" + expect(response.body).to include("footer-container") + end + end end diff --git a/spec/requests/stories_index_spec.rb b/spec/requests/stories_index_spec.rb index f733df249..7c82e5092 100644 --- a/spec/requests/stories_index_spec.rb +++ b/spec/requests/stories_index_spec.rb @@ -19,6 +19,12 @@ RSpec.describe "StoriesIndex", type: :request do expect(response.body).to include(CGI.escapeHTML(article.title)) end + it "renders registration page if site config is private" do + SiteConfig.public = false + get "/" + expect(response.body).to include("Great to have you") + end + it "renders proper description" do get "/" expect(response.body).to include(SiteConfig.community_description) @@ -334,6 +340,12 @@ RSpec.describe "StoriesIndex", type: :request do expect(response.body).to include("crayons-tabs__item crayons-tabs__item--current") end + it "renders properly even if site config is private" do + SiteConfig.public = false + get "/t/#{tag.name}" + expect(response.body).to include("crayons-tabs__item crayons-tabs__item--current") + end + it "has mod-action-button" do get "/t/#{tag.name}" expect(response.body).to include(' Date: Thu, 16 Jul 2020 10:22:49 -0400 Subject: [PATCH 3/4] Remove extra space --- app/models/site_config.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/site_config.rb b/app/models/site_config.rb index 1e1278758..56f4c786b 100644 --- a/app/models/site_config.rb +++ b/app/models/site_config.rb @@ -136,7 +136,6 @@ class SiteConfig < RailsSettings::Base # a public forem could have more fine-grained authentication (listings ar private etc.) in future field :public, type: :boolean, default: 1 - # Broadcast field :welcome_notifications_live_at, type: :date end From dcf3af7cffc76b46388098aaaa551309f2dcc38f Mon Sep 17 00:00:00 2001 From: benhalpern Date: Thu, 16 Jul 2020 15:16:18 -0400 Subject: [PATCH 4/4] Fix test copy --- spec/requests/internal/configs_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/requests/internal/configs_spec.rb b/spec/requests/internal/configs_spec.rb index a87e53b71..5193a4b0f 100644 --- a/spec/requests/internal/configs_spec.rb +++ b/spec/requests/internal/configs_spec.rb @@ -500,17 +500,17 @@ RSpec.describe "/internal/config", type: :request do end it "updates public to true" do - is_private = true - post "/internal/config", params: { site_config: { private: is_private }, + is_public = true + post "/internal/config", params: { site_config: { private: is_public }, confirmation: confirmation_message } - expect(SiteConfig.private).to eq(is_private) + expect(SiteConfig.public).to eq(is_public) end it "updates public to false" do - is_private = false - post "/internal/config", params: { site_config: { private: is_private }, + is_public = false + post "/internal/config", params: { site_config: { private: is_public }, confirmation: confirmation_message } - expect(SiteConfig.private).to eq(is_private) + expect(SiteConfig.public).to eq(is_public) end end end