diff --git a/Gemfile b/Gemfile index 03660c36cde..87af75514b6 100644 --- a/Gemfile +++ b/Gemfile @@ -178,7 +178,7 @@ gem 'aws-sdk-core', '~> 3.91.0' # File upload via fog + screenshots on travis gem 'aws-sdk-s3', '~> 1.61.0' -gem 'openproject-token', '~> 1.0.2' +gem 'openproject-token', '~> 2.0' gem 'plaintext', '~> 0.3.2' diff --git a/Gemfile.lock b/Gemfile.lock index 8e8556782a6..019edaaaee5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -654,7 +654,8 @@ GEM validate_email validate_url webfinger (>= 1.0.1) - openproject-token (1.0.2) + openproject-token (2.0) + activemodel parallel (1.19.1) parallel_tests (2.32.0) parallel @@ -1061,7 +1062,7 @@ DEPENDENCIES openproject-pdf_export! openproject-recaptcha! openproject-reporting! - openproject-token (~> 1.0.2) + openproject-token (~> 2.0) openproject-translations! openproject-two_factor_authentication! openproject-webhooks! diff --git a/app/controllers/enterprises_controller.rb b/app/controllers/enterprises_controller.rb index 7108a412ca5..3410d12dded 100644 --- a/app/controllers/enterprises_controller.rb +++ b/app/controllers/enterprises_controller.rb @@ -38,6 +38,7 @@ class EnterprisesController < ApplicationController before_action :youtube_content_security_policy before_action :require_admin before_action :check_user_limit, only: [:show] + before_action :check_domain, only: [:show] def show @current_token = EnterpriseToken.current @@ -121,4 +122,14 @@ class EnterprisesController < ApplicationController ) end end + + def check_domain + if OpenProject::Enterprise.token.try(:invalid_domain?) + flash.now[:error] = I18n.t( + "error_enterprise_token_invalid_domain", + expected: Setting.host_name, + actual: OpenProject::Enterprise.token.domain + ) + end + end end diff --git a/app/models/enterprise_token.rb b/app/models/enterprise_token.rb index 2739b39cde7..85842136668 100644 --- a/app/models/enterprise_token.rb +++ b/app/models/enterprise_token.rb @@ -56,14 +56,16 @@ class EnterpriseToken < ApplicationRecord validates_presence_of :encoded_token validate :valid_token_object + validate :valid_domain before_save :unset_current_token before_destroy :unset_current_token delegate :will_expire?, - :expired?, :subscriber, :mail, + :company, + :domain, :issued_at, :starts_at, :expires_at, @@ -84,6 +86,18 @@ class EnterpriseToken < ApplicationRecord RequestStore.delete :current_ee_token end + def expired? + token_object.expired? || invalid_domain? + end + + ## + # The domain is only validated for tokens from version 2.0 onwards. + def invalid_domain? + return false unless token_object&.validate_domain? + + token_object.domain != Setting.host_name + end + private def load_token! @@ -96,4 +110,8 @@ class EnterpriseToken < ApplicationRecord def valid_token_object errors.add(:encoded_token, :unreadable) unless load_token! end + + def valid_domain + errors.add :domain, :invalid if invalid_domain? + end end diff --git a/app/views/enterprises/_current.html.erb b/app/views/enterprises/_current.html.erb index a65b6f4187d..dfc7693b3ed 100644 --- a/app/views/enterprises/_current.html.erb +++ b/app/views/enterprises/_current.html.erb @@ -1,8 +1,8 @@ diff --git a/config/locales/en.yml b/config/locales/en.yml index 2702094a2c0..2edca8f4ab6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1102,6 +1102,7 @@ en: error_cookie_missing: 'The OpenProject cookie is missing. Please ensure that cookies are enabled, as this application will not properly function without.' error_custom_option_not_found: "Option does not exist." error_enterprise_activation_user_limit: "Your account could not be activated (user limit reached). Please contact your administrator to gain access." + error_enterprise_token_invalid_domain: "The Enterprise Edition is not active. Your Enterprise token's domain (%{actual}) does not match the system's host name (%{expected})." error_failed_to_delete_entry: 'Failed to delete this entry.' error_in_dependent: "Error attempting to alter dependent object: %{dependent_class} #%{related_id} - %{related_subject}: %{error}" error_invalid_selected_value: "Invalid selected value." diff --git a/spec/controllers/enterprises_controller_spec.rb b/spec/controllers/enterprises_controller_spec.rb index 5f270184ccc..41d2f1e99c7 100644 --- a/spec/controllers/enterprises_controller_spec.rb +++ b/spec/controllers/enterprises_controller_spec.rb @@ -30,14 +30,18 @@ require 'spec_helper' describe EnterprisesController, type: :controller do let(:a_token) { EnterpriseToken.new } - let(:token_object) do - token = OpenProject::Token.new - token.subscriber = 'Foobar' - token.mail = 'foo@example.org' - token.starts_at = Date.today - token.expires_at = nil - token + let(:token_attributes) do + { + subscriber: "Foobar", + mail: "foo@example.org", + starts_at: Date.today, + expires_at: nil + } + end + + let(:token_object) do + OpenProject::Token.new token_attributes end before do @@ -57,11 +61,53 @@ describe EnterprisesController, type: :controller do get :show end - it 'renders the overview' do - expect(response).to be_successful - expect(response).to render_template 'show' - expect(response).to render_template partial: 'enterprises/_current' - expect(response).to render_template partial: 'enterprises/_form' + shared_examples 'it renders the EE overview' do + it 'renders the overview' do + expect(response).to be_successful + expect(response).to render_template 'show' + expect(response).to render_template partial: 'enterprises/_current' + expect(response).to render_template partial: 'enterprises/_form' + end + end + + it_behaves_like 'it renders the EE overview' + + context 'with version >= 2.0' do + let(:token_attributes) { super().merge version: "2.0" } + + context 'with correct domain', with_settings: { host_name: 'community.openproject.com' } do + let(:token_attributes) { super().merge domain: 'community.openproject.com' } + + it_behaves_like 'it renders the EE overview' + + it "doesn't show any warnings or errors" do + expect(controller).not_to set_flash.now + end + end + + context 'with wrong domain', with_settings: { host_name: 'community.openproject.com' } do + let(:token_attributes) { super().merge domain: 'localhost' } + + it_behaves_like 'it renders the EE overview' + + it "shows an invalid domain error" do + expect(controller).to set_flash.now[:error].to(/.*localhost.*does not match.*community.openproject.com/) + end + end + end + + context 'with version < 2.0' do + let(:token_attributes) { super().merge version: "1.0.3" } + + context 'with wrong domain', with_settings: { host_name: 'community.openproject.com' } do + let(:token_attributes) { super().merge domain: 'localhost' } + + it_behaves_like 'it renders the EE overview' + + it "doesn't show any warnings or errors" do + expect(controller).not_to set_flash.now + end + end end end diff --git a/spec/features/admin/enterprise/enterprise_spec.rb b/spec/features/admin/enterprise/enterprise_spec.rb index c00530d26e0..d7f062ee4ed 100644 --- a/spec/features/admin/enterprise/enterprise_spec.rb +++ b/spec/features/admin/enterprise/enterprise_spec.rb @@ -38,6 +38,7 @@ describe 'Enterprise token', type: :feature, js: true do token.mail = 'foo@example.org' token.starts_at = Date.today token.expires_at = nil + token.domain = Setting.host_name token end @@ -79,9 +80,9 @@ describe 'Enterprise token', type: :feature, js: true do expect(page).to have_selector('.enterprise--active-token') expect(page.all('.attributes-key-value--key').map(&:text)) - .to eq ['Subscriber', 'Email', 'Maximum active users', 'Starts at', 'Expires at'] + .to eq ['Subscriber', 'Email', 'Domain', 'Maximum active users', 'Starts at', 'Expires at'] expect(page.all('.attributes-key-value--value').map(&:text)) - .to eq ['Foobar', 'foo@example.org', 'Unlimited', format_date(Date.today), 'Unlimited'] + .to eq ['Foobar', 'foo@example.org', Setting.host_name, 'Unlimited', format_date(Date.today), 'Unlimited'] expect(page).to have_selector('.button.icon-delete', text: I18n.t(:button_delete)) diff --git a/spec/features/admin/enterprise/token_domain_spec.rb b/spec/features/admin/enterprise/token_domain_spec.rb new file mode 100644 index 00000000000..44e9fe9ce57 --- /dev/null +++ b/spec/features/admin/enterprise/token_domain_spec.rb @@ -0,0 +1,126 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2020 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +require 'spec_helper' + +describe 'Enterprise Edition token domain', type: :feature, js: true do + let(:current_user) { FactoryBot.create :admin } + let(:ee_token) { File.read(Rails.root.join("spec/fixtures/ee_tokens/v2_1_user_localhost_3001.token")) } + + before do + allow(User).to receive(:current).and_return current_user + end + + shared_examples 'uploading a token' do + before do + visit '/admin/enterprise' + + fill_in "enterprise_token[encoded_token]", with: ee_token + + click_on "Save" + end + end + + describe 'initial upload' do + context 'with correct domain', with_settings: { host_name: 'localhost:3001' } do + it_behaves_like 'uploading a token' do + it 'saves the token' do + expect(body).to have_text 'Successful update.' + end + + it 'shows the token info' do + expect(body).to have_text 'operations@openproject.com' + end + end + end + + context 'with incorrect domain', with_settings: { host_name: 'localhost:3000' } do + it_behaves_like 'uploading a token' do + it "shows an invalid domain error" do + expect(body).to have_text "Domain is invalid." + end + end + end + end + + context 'with an active token', with_settings: { host_name: 'localhost:3001' } do + before do + allow(EnterpriseToken).to receive(:current).and_return(EnterpriseToken.new(encoded_token: ee_token)) + + visit '/admin/enterprise' + end + + shared_examples 'replacing a token' do + let(:new_token) { raise 'define me!' } + + before do + click_on "Replace your current support token" + + fill_in "enterprise_token[encoded_token]", with: new_token + + click_on "Save" + end + end + + it 'shows the current token info' do + expect(body).to have_text 'operations@openproject.com' + end + + describe 'replacing the token' do + context 'with correct domain' do + it_behaves_like 'replacing a token' do + let(:new_token) { ee_token } + + it 'updates the token' do + expect(body).to have_text 'Successful update.' + end + end + end + + context 'with incorrect domain' do + it_behaves_like 'replacing a token' do + let(:new_token) { File.read(Rails.root.join("spec/fixtures/ee_tokens/v2_1_user_localhost_3000.token")) } + + it 'shows an invalid domain error' do + expect(body).to have_text 'Domain is invalid.' + end + + it "shows the new token's info" do + expect(body).to have_text 'localhost:3000' + end + + it "but doesn't save the new token" do + visit '/admin/enterprise' + + expect(body).to have_text 'localhost:3001' + end + end + end + end + end +end diff --git a/spec/fixtures/ee_tokens/v2_1_user_localhost_3000.token b/spec/fixtures/ee_tokens/v2_1_user_localhost_3000.token new file mode 100644 index 00000000000..af00dc817a6 --- /dev/null +++ b/spec/fixtures/ee_tokens/v2_1_user_localhost_3000.token @@ -0,0 +1,42 @@ +-----BEGIN OPENPROJECT-EE TOKEN----- +eyJkYXRhIjoiMVUxUDRJbFR4RmZzaThTZVFFbnU2SlZRZ1BWVVZta3JnWERw +VlVCVnBpZHJjUklZTEN6b1pTWWNsc2dyXG5CaStKTUhKZS9lWTIrQUtuSDVI +Q1BFc3loMXFhUnlXRWlqbTBCUDJTbVBmeXJ2UXRuay9ZbDF0TVp2SndcbjIx +ZHRIeXVvQW05bHdlZStOWEtBS2xWWmNrZjNYb3B5VXQ0Z2xDclB4UGRnU1ht +b3JySFB2UkFoOFBkQlxuTEhLRlVCNU1mcEMyVmlPeTZUS2ZJYjd1ajBhdVEv +QlBIU2lpRjhZb3BmcEVldzBZazR2SUY2Rnk5WDdLXG5ieExkZ3U1VTZQSVAv +T3paZnM5cVpFSFAxcUwwclBwWlM0d21iV1dtZ21yME1WTW91QTBzU1lvdlQ0 +VlpcbnBCUXNBMXpWZFl1K2lFSHpQd2JXXG4iLCJrZXkiOiJiR2hwbjFEcjBN +ZHZnQjNSTHNWVkRjaHNaSWlYQWo5Mk9XcTRPUnZDVmtuQXNVSWFpdlNMMXky +QUtzY0dcbkJMR3FjS2RuWHNQS2JXeWx2NTcrdzI5TGIwWHoxMkVzclFYQTQ3 +Sk1BaFI5bnpoNkM4blMxVEs3Y1R3eVxubzUyMGZCL2VxY2pUWTRQNzdGQWRx +OTRGOTdLNkl2QnhLWFkxdVVGaVRPUzA2c3dyUTNtRW5rZ0t6ZkFvXG5hamhi +TGcrWnB2R2FLT1ZOZWFQKzdlbUJjSFRZbFhNMnY2S0V2QXYzMzJ1UHVuOVlS +aDZaeTVya2dHTmlcbitEcDZIM2M0Mzd5TjdYZGZhVHdaSWZzYldudWpNMmNO +L25hYk53cDVMYytwL0dFaEVmYnlWRENJTnZvaVxubmh1OGowV0RrL3RVa3BM +MENBWmk5Uk83dXZGcHdJSkluZ0E3dlZ3NVdVOFdScWNKcnlUZkVPVlYyVk15 +XG5GV2RnOUlzbmpuNW02MjdJbDQ5eDlTUXIxVzBsR285RFZYYXZpNzI1em95 +RG11eXlRQy9rbC91dHZBYzNcbjY0QnlKVWpET3BNM1dDRG5jWEVqZnUwQzNx +UGNka1dvTi9wcmg4Q1RqWlFiNkhsdDhKRWxHUk8zaThxSFxuZGU2Z0hpaGlZ +NlZzNkZzcTdhZGxPb0JUZGZJOURNUnV1eSs5cVZIUld4VDBqaFNjeTZkbnA1 +MW1hMUdGXG5yNnJWSWVHMWF4Zm1aV3I0L0tpRVEvM1VRUVFBbXByQm9hYita +OThpaG5vdFhseW80dDRONWZuR0JCVWFcbktQem4rNlJoL1l4YnFNMG1Sa1Jr +aVJVMkpUcHNyWStIQzdlOEt3M09YL1pESjJOWlFTcWZjS0l6dlR5eFxuSG1G +eTVCdDdrSzltMnduT2FMOXo1dldQVG5tRWZEWGQvekFhVC95TU5VUEwyWFQ2 +a29GNXlKSENNQnJEXG5aNDBjT0JzYnNhMTRqTExqS05kc2Y0RnlwR2N3K3lE +dG94U2o0STMvaTJPeUpGaEV0QXVYbGVvYUhTN2xcbmhpR0VGdEhVUXVyNGVO +WE9VM3I4SHNiQi9qWUxCSlVLWDREQVhVNGQvakNDa1FUaU1weTZzZWVjYkRm +bVxuZ0lhQVFjcGhPMk5xMWd3c2h6RlM4L1p1MXpkMDF4NnhaOWxwMnRSYjFZ +RmFQaUU3eVpqVFdlUzY5MUVkXG5mSmdNOXNCZENjUkUvOW0wYWc4SzFjQzg0 +T3lFT3p3S2NHcWJnSlUzNW41Y2tURWpjK2tQU0I5NTk1MTZcbit3Mlpwek0z +b1VmZDFBc29BUXdZOHhTS3hXM0ZEdGNkQzUwMThnQWJnRDRTcFdKRXlwTld5 +QnBya2RCSFxuY2J6ZHdHOWxvTjNoMEpsNWFiL2FuQWt4SHR1MHVWUTVhcjZw +cGhiRXRaQmFpTUtscDhXYVUzSkJ3TWZmXG5UdjFLWVdjVE95U3hHTnZBY3ZO +ZjVkTS9WdGxDVEpjaUpheGVqZTJDZmhWWk5sSVAycE9hTCs4N0lzSmdcblFN +cXBIY0dwNzNvZ0JFK0x0UlZYQXRzZy9xTVVkeVdtN1l3MStQQWxqTENLTDNG +SUduZUVqdlIxdjhJU1xuVklyOXhIL3ZoUFB0emFwRGt0TWJ1TnRwMDlsRFFQ +QjNKT0dwZGFFSlQ0THhpdHhjenZhQ051S1A2aDhzXG44b0ZFNDE5ME9DSExN +d2VtYVR5SDRCQ2RaV3lvR1ZKMlZmU0d3Y3ZzY2t1akI3blBNS21vT3cvUE5U +SXRcbjFVRnNOTkZFSDRpbW4zaUkrS2hPTmRnL3Y1WUY0Zz09XG4iLCJpdiI6 +IklrUmtubGNoU0VwNXh2NFErSzdrZUE9PVxuIn0= +-----END OPENPROJECT-EE TOKEN----- diff --git a/spec/fixtures/ee_tokens/v2_1_user_localhost_3001.token b/spec/fixtures/ee_tokens/v2_1_user_localhost_3001.token new file mode 100644 index 00000000000..16eee40d89e --- /dev/null +++ b/spec/fixtures/ee_tokens/v2_1_user_localhost_3001.token @@ -0,0 +1,42 @@ +-----BEGIN OPENPROJECT-EE TOKEN----- +eyJkYXRhIjoiS3BEYlEybnZzeTIzVUpGUkpOSWhidkl4OWNXKy9GeDR3NXJL +QTU2UitWS3ZxYjZrWjdua2Z6VnRzNzJXXG50aklFQmdNODdEb2dMS2xxbVRT +TG5lenE4WjJlL3RjQVBlT1NVRlZNMWovaGNWZFFJYzY5LzBiZXM2OXpcbnpS +bHpNOFdDQnE2R3lNYTdzV2JZMVJQM2FjaCtmZ08wOWdsam9ESXdpUzZ1eWlL +ZFdYRnRzNHMwRUUxUFxuRHlOSUl5dnR4UXB6dlMzQ1o3MXpzZkZNMFl3d0Jp +UTlZQ1I2V3NaOXNzRzJnR011NjI4Q05TdnJwVmcxXG5Tb1dWZE1KbkRPanFz +NlM3aDdiVWR0Y3lESFViVlNJcmRPRHNxTG4yeEQzZzE3ajdUb1lnQ21hUlc0 +MTVcbkg3VEVlOGN2UEhvZGhTMFh6WkJsXG4iLCJrZXkiOiJNVjZldXdqS1J6 +TmMzS0YxY3hzMTRkTUhUKzQxYXFOcnc3YjE2UDZBS04rcWdBbU5FUTk5S2pm +VFJWaEVcbmFxVmEzQzhoemhZRW85UEZFbW1TT3BZN3A4aFRTK01DT2lyMUo4 +R05GT2w5NzRUN3dUVVNzMDdWQnNlNVxuTzdGQkRMNDUzREJOandSQy9XTVpV +RWtyR2xiYjNQYndWcWpkOU5pRWFPWk1xcU84WVJDRnUzdVBMWXBrXG5WM3hl +MXJsZ3FuZUcwWHRUdC80MWVxOENuWFpyS0tKRVlRNTZNaE9QMVM0VkNXTjVl +VGxOeEVIdkJFb0hcbms5Z2NkSEZuTkxNeVdEK01DTXNQazM0SWtFaGJQN3RP +SFRrMGZpaGpTejlIL0tpRmIycWNTdmJ0M2lJQlxuUVhSUjc5L0lVeHZzcmcx +K0o2a25qSlNGaXlERS9sdXV0dVdhRzBEM29EM2dYWmVKbzZIZEhiU3RMbVhj +XG41MkJtemRZaTF3RXI1Um1HU2lZV0V3dTkvY2VRTWVxTldVQTB4OElTamRj +eXU2NTgxeTZidGdKSXg3aEtcbjcxTGNWOFJKN1hlQ1RmQ2ZVcUdVMkVkbkVr +MlpjaFozb1dXOGZya2ZGMWkreDQ1Zm5KVFdNMXM0Smlva1xuUkwwajJrWXVR +U2RFYlhwSWRnREpHNmVHTjZxd0VpbFVVN1E2Z1Vzcm1RbG9mUk90dUgydFA2 +UFNhaUhIXG5nb3Q4LzZydDFQT29zR2RidlAwNjNBU2h2SmRTQXpoQXRzckQv +VVRBVDFITkVlZjBNWFcrdS95b3FKRkpcbmdLdm96ZnQxVDFjWURaWno1ZlBy +ajVYQlI5VE5JVmdNYUY3R3AraUtWWDYxbkE2am03OFlDSExaN1hXbVxudXlq +eDBxb2hJenY3ZzBIaU5pMTV4MytOdEJkTkEzZXB3SEJrRjZQemFRL29UOWx3 +cWphbnhxQzRHaVQ5XG5yYm81QTd2SytMNE5XeHBidzJGK0k3WDJNc2FNUU52 +eXRDTjFDWXFqcGJibnRzb3dQcUY4NXdHMWtRTkhcbnJSVGdZa3lvZ3RENmxR +T1pLZFlJNjlIVGlKaW5GSW1QR2FTeXp2OHliMzljQWVGcFlET2h2Z2czS1Z1 +MVxuL0pQMzVVekNRRXEyV1ZzUjVaOWtmdzFFd3FaNzE1a2hEVm04U0Mzc2Rw +d241N2I4UTBUcUxzSnEvQlpRXG40bytWZFlONnAvL3VXWHlVQXF6MDJVeWp3 +VGw5ZDJwdWJPUGE2bUQySnY2WEIzck1qUSt2alh5NytsbG5cbmVBaUZzWmJu +Q0t3R2VWNDY3Y0ZjdXIvOTFmejg3Q0FRV1pOc0F0dDBxaXVlR1VTZ2JSSmgx +bUhERDJCMVxuM0RreHlmRms5dWtWUjNRemdJNk9LMStMM0FJbU42ckxUMzlV +NVVPeFZ3N2YrYktmQlVScmxzckZscWZwXG5OdGhyUEpxVTJCT3U4MUE5QnR0 +SnlFTm5wdG4xcWY3bG9QSzBaV21WSW5xTmJQbXpndEdRbzRRaDN4MTlcbis4 +Z2FHandZaUlBYk02bS84T1BhK3ZndFNZckFCNjR1aHlIbkJETTl5ekIzVGxT +WUlSTlcxVkF3RUNaNVxuRE91VWQxcFNnWC9TbzZjbUpoSHA1S2toQnJVQVFw +K24zZWZFU2JXdi9RcTljL2dBc2o3N3VZOGFlcWk1XG51cFNxa1FLMGpMd1hv +K1luRjRnY08reklBR1V5RHRYb2pBWVErSDJRMTVhdFE5ZjlhclpSZUlEWG1j +THBcblMvU204RjVUQm5Td1lQbGovSGQvUDA0RkhEclBHQT09XG4iLCJpdiI6 +IkpvUXFxU1BmREI4dDNKWW1qYWVJRmc9PVxuIn0= +-----END OPENPROJECT-EE TOKEN----- diff --git a/spec/models/enterprise_token_spec.rb b/spec/models/enterprise_token_spec.rb index 9608cba8f31..771465121cd 100644 --- a/spec/models/enterprise_token_spec.rb +++ b/spec/models/enterprise_token_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' RSpec.describe EnterpriseToken, type: :model do - let(:object) { OpenProject::Token.new } + let(:object) { OpenProject::Token.new domain: Setting.host_name } subject { EnterpriseToken.new(encoded_token: 'foo') } before do