Initial commit
This commit is contained in:
@@ -0,0 +1,727 @@
|
||||
require_relative '../../spec_helper_min'
|
||||
|
||||
describe Admin::OrganizationUsersController do
|
||||
include_context 'organization with users helper'
|
||||
include Rack::Test::Methods
|
||||
include Warden::Test::Helpers
|
||||
|
||||
before(:each) do
|
||||
host! "#{@organization.name}.localhost.lan"
|
||||
end
|
||||
|
||||
let(:username) { unique_name('user') }
|
||||
|
||||
let(:user_params) do
|
||||
{
|
||||
username: username,
|
||||
email: "#{username}@org.com",
|
||||
password: 'user-1',
|
||||
password_confirmation: 'user-1',
|
||||
quota_in_bytes: 1000,
|
||||
twitter_datasource_enabled: false
|
||||
}
|
||||
end
|
||||
|
||||
describe 'security' do
|
||||
before(:all) do
|
||||
@org_user_2.org_admin = true
|
||||
@org_user_2.save
|
||||
|
||||
@owner = @org_user_owner
|
||||
@admin = @org_user_2
|
||||
@user = @org_user_1
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
User.any_instance.stubs(:validate_credentials_not_taken_in_central).returns(true)
|
||||
User.any_instance.stubs(:create_in_central).returns(true)
|
||||
User.any_instance.stubs(:update_in_central).returns(true)
|
||||
User.any_instance.stubs(:delete_in_central).returns(true)
|
||||
User.any_instance.stubs(:load_common_data).returns(true)
|
||||
User.any_instance.stubs(:reload_avatar).returns(true)
|
||||
end
|
||||
|
||||
describe '#show' do
|
||||
it 'returns 404 for non admin users' do
|
||||
login_as(@user, scope: @user.username)
|
||||
|
||||
get organization_users_url(user_domain: @user.username)
|
||||
last_response.status.should == 404
|
||||
end
|
||||
|
||||
it 'returns 200 for admin users' do
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
get organization_users_url(user_domain: @admin.username)
|
||||
last_response.status.should == 200
|
||||
end
|
||||
|
||||
it 'returns 200 for owner' do
|
||||
login_as(@owner, scope: @owner.username)
|
||||
|
||||
get organization_users_url(user_domain: @owner.username)
|
||||
last_response.status.should == 200
|
||||
end
|
||||
end
|
||||
|
||||
describe '#new' do
|
||||
it 'returns 404 for non admin users' do
|
||||
login_as(@user, scope: @user.username)
|
||||
|
||||
get new_organization_user_url(user_domain: @user.username)
|
||||
last_response.status.should == 404
|
||||
end
|
||||
|
||||
it 'returns 200 for admin users' do
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
get new_organization_user_url(user_domain: @admin.username)
|
||||
last_response.status.should == 200
|
||||
end
|
||||
|
||||
it 'returns 200 for owner' do
|
||||
login_as(@owner, scope: @owner.username)
|
||||
|
||||
get new_organization_user_url(user_domain: @owner.username)
|
||||
last_response.status.should == 200
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create' do
|
||||
after(:each) do
|
||||
Carto::User.find_by_username(user_params[:username]).try(:destroy)
|
||||
end
|
||||
|
||||
it 'fails if password is the username' do
|
||||
login_as(@owner, scope: @owner.username)
|
||||
|
||||
post create_organization_user_url(user_domain: @owner.username),
|
||||
user: user_params.merge(password: username, password_confirmation: username),
|
||||
password_confirmation: @owner.password
|
||||
|
||||
last_response.status.should == 200
|
||||
last_response.body.should include 'must be different than the user name'
|
||||
end
|
||||
|
||||
it 'fails if password is a common one' do
|
||||
login_as(@owner, scope: @owner.username)
|
||||
|
||||
post create_organization_user_url(user_domain: @owner.username),
|
||||
user: user_params.merge(password: 'galina', password_confirmation: 'galina'),
|
||||
password_confirmation: @owner.password
|
||||
|
||||
last_response.status.should == 200
|
||||
last_response.body.should include "can't be a common password"
|
||||
end
|
||||
|
||||
it 'fails if password is not strong' do
|
||||
@owner.organization.stubs(:strong_passwords_enabled).returns(true)
|
||||
login_as(@owner, scope: @owner.username)
|
||||
|
||||
post create_organization_user_url(user_domain: @owner.username),
|
||||
user: user_params.merge(password: 'galinaa', password_confirmation: 'galinaa'),
|
||||
password_confirmation: @owner.password
|
||||
|
||||
last_response.status.should == 200
|
||||
last_response.body.should include 'must be at least 8 characters long'
|
||||
@owner.organization.unstub(:strong_passwords_enabled)
|
||||
end
|
||||
|
||||
it 'returns 404 for non admin users' do
|
||||
login_as(@user, scope: @user.username)
|
||||
|
||||
post create_organization_user_url(user_domain: @user.username), user: user_params
|
||||
last_response.status.should == 404
|
||||
end
|
||||
|
||||
it 'returns 200 for admin users trying to create an admin' do
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
post create_organization_user_url(user_domain: @admin.username),
|
||||
user: user_params.merge(org_admin: true),
|
||||
password_confirmation: @admin.password
|
||||
last_response.status.should == 200
|
||||
last_response.body.should include 'Validation failed: org_admin can only be set by organization owner'
|
||||
end
|
||||
|
||||
it 'returns 403 for admin users trying to create an admin if wrong password_confirmation' do
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
post create_organization_user_url(user_domain: @admin.username),
|
||||
user: user_params.merge(org_admin: true),
|
||||
password_confirmation: 'wrong'
|
||||
last_response.status.should == 403
|
||||
last_response.body.should include 'Confirmation password sent does not match your current password'
|
||||
end
|
||||
|
||||
it 'returns 302 for admin users trying to create a non-admin' do
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
post create_organization_user_url(user_domain: @admin.username),
|
||||
user: user_params,
|
||||
password_confirmation: @admin.password
|
||||
last_response.status.should == 302
|
||||
end
|
||||
|
||||
it 'returns 302 for owner' do
|
||||
login_as(@owner, scope: @owner.username)
|
||||
|
||||
post create_organization_user_url(user_domain: @owner.username),
|
||||
user: user_params.merge(org_admin: true),
|
||||
password_confirmation: @owner.password
|
||||
last_response.status.should == 302
|
||||
end
|
||||
end
|
||||
|
||||
describe '#edit' do
|
||||
it 'returns 404 for non admin users' do
|
||||
login_as(@user, scope: @user.username)
|
||||
|
||||
get edit_organization_user_url(user_domain: @user.username, id: @user.username)
|
||||
last_response.status.should == 404
|
||||
end
|
||||
|
||||
it 'returns 403 for admin users trying to edit an admin' do
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
get edit_organization_user_url(user_domain: @admin.username, id: @owner.username)
|
||||
last_response.status.should == 403
|
||||
end
|
||||
|
||||
it 'returns 200 for admin users trying to edit a non-admin' do
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
get edit_organization_user_url(user_domain: @admin.username, id: @user.username)
|
||||
last_response.status.should == 200
|
||||
end
|
||||
|
||||
it 'returns 200 for admin users trying to edit themselves' do
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
get edit_organization_user_url(user_domain: @admin.username, id: @admin.username)
|
||||
last_response.status.should == 200
|
||||
end
|
||||
|
||||
it 'returns 200 for owner' do
|
||||
login_as(@owner, scope: @owner.username)
|
||||
|
||||
get edit_organization_user_url(user_domain: @owner.username, id: @admin.username)
|
||||
last_response.status.should == 200
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update' do
|
||||
it 'returns 404 for non admin users' do
|
||||
login_as(@user, scope: @user.username)
|
||||
|
||||
put update_organization_user_url(user_domain: @user.username, id: @user.username), user: { quota_in_bytes: 7 }
|
||||
last_response.status.should == 404
|
||||
end
|
||||
|
||||
it 'returns 403 for admin users trying to edit an admin' do
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
put update_organization_user_url(user_domain: @admin.username, id: @owner.username),
|
||||
user: { quota_in_bytes: 7 },
|
||||
password_confirmation: @admin.password
|
||||
last_response.status.should == 403
|
||||
end
|
||||
|
||||
it 'returns 200 with error for admin users trying to convert a user into an admin' do
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
put update_organization_user_url(user_domain: @admin.username, id: @user.username),
|
||||
user: { org_admin: true },
|
||||
password_confirmation: @admin.password
|
||||
last_response.status.should == 422
|
||||
expect(last_response.body).to include 'org_admin can only be set by organization owner'
|
||||
end
|
||||
|
||||
it 'returns 403 if wrong password_confirmation' do
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
put update_organization_user_url(user_domain: @admin.username, id: @user.username),
|
||||
user: { quota_in_bytes: 7 },
|
||||
password_confirmation: 'wrong'
|
||||
last_response.status.should == 403
|
||||
last_response.body.should include 'Confirmation password sent does not match your current password'
|
||||
end
|
||||
|
||||
it 'returns 302 for admin users trying to edit a non-admin' do
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
put update_organization_user_url(user_domain: @admin.username, id: @user.username),
|
||||
user: { quota_in_bytes: 7 },
|
||||
password_confirmation: @admin.password
|
||||
last_response.status.should == 302
|
||||
end
|
||||
|
||||
it 'returns 302 for admin users trying to edit themselves' do
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
put update_organization_user_url(user_domain: @admin.username, id: @admin.username),
|
||||
user: { quota_in_bytes: 7 },
|
||||
password_confirmation: @admin.password
|
||||
last_response.status.should == 302
|
||||
end
|
||||
|
||||
it 'returns 302 for owner' do
|
||||
login_as(@owner, scope: @owner.username)
|
||||
|
||||
put update_organization_user_url(user_domain: @owner.username, id: @admin.username),
|
||||
user: { quota_in_bytes: 7 },
|
||||
password_confirmation: @owner.password
|
||||
last_response.status.should == 302
|
||||
end
|
||||
|
||||
it 'fails if password is the username' do
|
||||
login_as(@owner, scope: @owner.username)
|
||||
|
||||
put update_organization_user_url(user_domain: @owner.username, id: @admin.username),
|
||||
user: { password: @admin.username, password_confirmation: @admin.username },
|
||||
password_confirmation: @owner.password
|
||||
|
||||
last_response.status.should == 422
|
||||
last_response.body.should include 'must be different than the user name'
|
||||
end
|
||||
|
||||
it 'fails if password is a common one' do
|
||||
login_as(@owner, scope: @owner.username)
|
||||
|
||||
put update_organization_user_url(user_domain: @owner.username, id: @admin.username),
|
||||
user: { password: 'galina', password_confirmation: 'galina' },
|
||||
password_confirmation: @owner.password
|
||||
|
||||
last_response.status.should == 422
|
||||
last_response.body.should include "can't be a common password"
|
||||
end
|
||||
|
||||
it 'fails if password is not strong' do
|
||||
Organization.any_instance.stubs(:strong_passwords_enabled).returns(true)
|
||||
login_as(@owner, scope: @owner.username)
|
||||
|
||||
put update_organization_user_url(user_domain: @owner.username, id: @admin.username),
|
||||
user: { password: 'galinaa', password_confirmation: 'galinaa' },
|
||||
password_confirmation: @owner.password
|
||||
|
||||
last_response.status.should == 422
|
||||
last_response.body.should include 'must be at least 8 characters long'
|
||||
Organization.any_instance.unstub(:strong_passwords_enabled)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#destroy' do
|
||||
it 'returns 404 for non admin users' do
|
||||
login_as(@user, scope: @user.username)
|
||||
|
||||
delete delete_organization_user_url(user_domain: @user.username, id: @user.username),
|
||||
password_confirmation: @user.password
|
||||
last_response.status.should == 404
|
||||
end
|
||||
|
||||
it 'returns 403 for admin users trying to destroy an admin' do
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
delete delete_organization_user_url(user_domain: @admin.username, id: @owner.username),
|
||||
password_confirmation: @admin.password
|
||||
last_response.status.should == 403
|
||||
end
|
||||
|
||||
it 'returns 302 for admin users trying to destroy a non-admin' do
|
||||
doomed_user = FactoryGirl.create(:valid_user, organization: @organization)
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
delete delete_organization_user_url(user_domain: @admin.username, id: doomed_user.username),
|
||||
password_confirmation: @admin.password
|
||||
last_response.status.should == 302
|
||||
end
|
||||
|
||||
it 'returns error if wrong password_confirmation' do
|
||||
doomed_user = FactoryGirl.create(:valid_user, organization: @organization)
|
||||
login_as(@admin, scope: @admin.username)
|
||||
|
||||
delete delete_organization_user_url(user_domain: @admin.username, id: doomed_user.username),
|
||||
password_confirmation: 'wrong'
|
||||
last_response.status.should == 302
|
||||
follow_redirect!
|
||||
last_response.body.should include 'Confirmation password sent does not match your current password'
|
||||
end
|
||||
|
||||
it 'returns 302 for owner' do
|
||||
doomed_user = FactoryGirl.create(:valid_user, organization: @organization, org_admin: true)
|
||||
login_as(@owner, scope: @owner.username)
|
||||
|
||||
delete delete_organization_user_url(
|
||||
user_domain: @owner.username,
|
||||
id: doomed_user.username
|
||||
), password_confirmation: @owner.password
|
||||
last_response.status.should == 302
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'owner behaviour' do
|
||||
before(:each) do
|
||||
User.any_instance.stubs(:update_in_central).returns(true)
|
||||
User.any_instance.stubs(:create_in_central).returns(true)
|
||||
login_as(@org_user_owner, scope: @org_user_owner.username)
|
||||
end
|
||||
|
||||
describe '#new' do
|
||||
it 'quota defaults to organization default' do
|
||||
expected_quota = 123456789
|
||||
Organization.any_instance.stubs(:default_quota_in_bytes).returns(expected_quota)
|
||||
|
||||
get new_organization_user_url(user_domain: @org_user_owner.username)
|
||||
last_response.status.should eq 200
|
||||
|
||||
last_response.body.should include 123456789.to_s
|
||||
end
|
||||
|
||||
it 'quota defaults to remaining quota if the assigned default goes overquota' do
|
||||
expected_quota = @organization.unassigned_quota
|
||||
Organization.any_instance.stubs(:default_quota_in_bytes).returns(123456789012345)
|
||||
|
||||
get new_organization_user_url(user_domain: @org_user_owner.username)
|
||||
last_response.status.should eq 200
|
||||
|
||||
last_response.body.should include expected_quota.to_s
|
||||
end
|
||||
end
|
||||
|
||||
describe '#show' do
|
||||
it 'returns 200 for organization owner users' do
|
||||
get organization_users_url(user_domain: @org_user_owner.username)
|
||||
last_response.status.should == 200
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create' do
|
||||
it 'creates users' do
|
||||
::User.any_instance.stubs(:create_in_central).returns(true)
|
||||
User.any_instance.expects(:load_common_data).once.returns(true)
|
||||
|
||||
post create_organization_user_url(user_domain: @org_user_owner.username),
|
||||
user: user_params,
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.status.should eq 302
|
||||
|
||||
user = Carto::User.find_by_username(user_params[:username])
|
||||
user.email.should eq user_params[:email]
|
||||
user.quota_in_bytes.should eq user_params[:quota_in_bytes]
|
||||
user.twitter_datasource_enabled.should be_nil
|
||||
user.builder_enabled.should be_nil
|
||||
user.engine_enabled.should be_nil
|
||||
|
||||
user.destroy
|
||||
end
|
||||
end
|
||||
|
||||
describe 'existing user operations' do
|
||||
before(:each) do
|
||||
@existing_user = FactoryGirl.create(:carto_user, organization: @carto_organization, password: 'abcdefgh')
|
||||
end
|
||||
|
||||
describe '#update' do
|
||||
after(:each) do
|
||||
::User[@existing_user.id].destroy
|
||||
end
|
||||
|
||||
it 'updates users' do
|
||||
new_quota = @existing_user.quota_in_bytes * 2
|
||||
put update_organization_user_url(user_domain: @org_user_owner.username, id: @existing_user.username),
|
||||
user: { quota_in_bytes: new_quota },
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.status.should eq 302
|
||||
|
||||
@existing_user.reload
|
||||
@existing_user.quota_in_bytes.should eq new_quota
|
||||
end
|
||||
|
||||
it 'does not update users in case of Central failure' do
|
||||
::User.any_instance.stubs(:update_in_central).raises(CartoDB::CentralCommunicationFailure.new('Failed'))
|
||||
new_quota = @existing_user.quota_in_bytes * 2
|
||||
put update_organization_user_url(user_domain: @org_user_owner.username, id: @existing_user.username),
|
||||
user: { quota_in_bytes: new_quota },
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.body.should include('There was a problem while updating this user.')
|
||||
|
||||
@existing_user.reload
|
||||
@existing_user.quota_in_bytes.should_not eq new_quota
|
||||
end
|
||||
|
||||
it 'validates before updating in Central' do
|
||||
::User.any_instance.stubs(:update_in_central).never
|
||||
params = {
|
||||
password: 'zyx',
|
||||
confirm_password: 'abc'
|
||||
}
|
||||
|
||||
put update_organization_user_url(user_domain: @org_user_owner.username, id: @existing_user.username),
|
||||
user: params,
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.body.should include('match confirmation')
|
||||
end
|
||||
|
||||
it 'cannot update password if it does not change old_password' do
|
||||
last_change = @existing_user.last_password_change_date
|
||||
::User.any_instance.stubs(:update_in_central).never
|
||||
params = {
|
||||
password: 'abcdefgh',
|
||||
confirm_password: 'abcdefgh'
|
||||
}
|
||||
|
||||
put update_organization_user_url(user_domain: @org_user_owner.username, id: @existing_user.username),
|
||||
user: params
|
||||
@existing_user.reload
|
||||
@existing_user.last_password_change_date.should eq last_change
|
||||
end
|
||||
|
||||
it 'creates a multifactor authentication' do
|
||||
put update_organization_user_url(user_domain: @org_user_owner.username, id: @existing_user.username),
|
||||
user: { mfa: '1' },
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.status.should eq 302
|
||||
|
||||
@existing_user.reload
|
||||
@existing_user.user_multifactor_auths.should_not be_empty
|
||||
end
|
||||
|
||||
it 'removes the multifactor authentications' do
|
||||
FactoryGirl.create(:totp, user: @existing_user)
|
||||
@existing_user.reload.user_multifactor_auths.should_not be_empty
|
||||
|
||||
put update_organization_user_url(user_domain: @org_user_owner.username, id: @existing_user.username),
|
||||
user: { mfa: '0' },
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.status.should eq 302
|
||||
|
||||
@existing_user.reload
|
||||
@existing_user.user_multifactor_auths.should be_empty
|
||||
end
|
||||
|
||||
it 'does not update the user multifactor authentications if the user saving operation fails' do
|
||||
User.any_instance.stubs(:save).raises(Sequel::ValidationFailed.new('error!'))
|
||||
|
||||
put update_organization_user_url(user_domain: @org_user_owner.username, id: @existing_user.username),
|
||||
user: { mfa: '1' },
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.status.should eq 422
|
||||
|
||||
@existing_user.reload
|
||||
@existing_user.user_multifactor_auths.should be_empty
|
||||
end
|
||||
|
||||
it 'does not save the user if the multifactor authentication updating operation fails' do
|
||||
mfa = Carto::UserMultifactorAuth.new
|
||||
Carto::UserMultifactorAuth.stubs(:create!).raises(ActiveRecord::RecordInvalid.new(mfa))
|
||||
|
||||
@existing_user.expects(:save).never
|
||||
|
||||
put update_organization_user_url(user_domain: @org_user_owner.username, id: @existing_user.username),
|
||||
user: { mfa: '1' },
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.status.should eq 422
|
||||
end
|
||||
end
|
||||
|
||||
describe '#destroy' do
|
||||
it 'deletes users' do
|
||||
delete delete_organization_user_url(user_domain: @org_user_owner.username, id: @existing_user.username),
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.status.should eq 302
|
||||
|
||||
::User[@existing_user.id].should be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'soft limits' do
|
||||
before(:each) do
|
||||
User.any_instance.stubs(:load_common_data).returns(true)
|
||||
end
|
||||
|
||||
def soft_limit_values(value = nil,
|
||||
soft_geocoding_limit: nil,
|
||||
soft_here_isolines_limit: nil,
|
||||
soft_obs_snapshot_limit: nil,
|
||||
soft_obs_general_limit: nil,
|
||||
soft_twitter_datasource_limit: nil)
|
||||
values = Hash.new(value)
|
||||
values[:soft_geocoding_limit] = soft_geocoding_limit unless soft_geocoding_limit.nil?
|
||||
values[:soft_here_isolines_limit] = soft_here_isolines_limit unless soft_here_isolines_limit.nil?
|
||||
values[:soft_obs_snapshot_limit] = soft_obs_snapshot_limit unless soft_obs_snapshot_limit.nil?
|
||||
values[:soft_obs_general_limit] = soft_obs_general_limit unless soft_obs_general_limit.nil?
|
||||
values[:soft_twitter_datasource_limit] = soft_twitter_datasource_limit unless soft_twitter_datasource_limit.nil?
|
||||
values
|
||||
end
|
||||
|
||||
def update_soft_limits(user, value,
|
||||
soft_geocoding_limit: nil,
|
||||
soft_here_isolines_limit: nil,
|
||||
soft_obs_snapshot_limit: nil,
|
||||
soft_obs_general_limit: nil,
|
||||
soft_twitter_datasource_limit: nil)
|
||||
|
||||
values = soft_limit_values(value,
|
||||
soft_geocoding_limit: soft_geocoding_limit,
|
||||
soft_here_isolines_limit: soft_here_isolines_limit,
|
||||
soft_obs_snapshot_limit: soft_obs_snapshot_limit,
|
||||
soft_obs_general_limit: soft_obs_general_limit,
|
||||
soft_twitter_datasource_limit: soft_twitter_datasource_limit)
|
||||
|
||||
old_limits = {
|
||||
soft_geocoding_limit: user.soft_geocoding_limit,
|
||||
soft_here_isolines_limit: user.soft_here_isolines_limit,
|
||||
soft_obs_snapshot_limit: user.soft_obs_snapshot_limit,
|
||||
soft_obs_general_limit: user.soft_obs_general_limit,
|
||||
soft_twitter_datasource_limit: user.soft_twitter_datasource_limit
|
||||
}
|
||||
|
||||
user.soft_geocoding_limit = values[:soft_geocoding_limit]
|
||||
user.soft_here_isolines_limit = values[:soft_here_isolines_limit]
|
||||
user.soft_obs_snapshot_limit = values[:soft_obs_snapshot_limit]
|
||||
user.soft_obs_general_limit = values[:soft_obs_general_limit]
|
||||
user.soft_twitter_datasource_limit = values[:soft_twitter_datasource_limit]
|
||||
user.save
|
||||
user.reload
|
||||
|
||||
old_limits
|
||||
end
|
||||
|
||||
def check_soft_limits(user, value)
|
||||
values = soft_limit_values(value)
|
||||
|
||||
user.soft_geocoding_limit.should eq values[:soft_geocoding_limit]
|
||||
user.soft_here_isolines_limit.should eq values[:soft_here_isolines_limit]
|
||||
user.soft_obs_snapshot_limit.should eq values[:soft_obs_snapshot_limit]
|
||||
user.soft_obs_general_limit.should eq values[:soft_obs_general_limit]
|
||||
user.soft_twitter_datasource_limit.should eq values[:soft_twitter_datasource_limit]
|
||||
end
|
||||
|
||||
def soft_limits_params(value)
|
||||
values = soft_limit_values(value)
|
||||
{
|
||||
soft_geocoding_limit: values[:soft_geocoding_limit],
|
||||
soft_here_isolines_limit: values[:soft_here_isolines_limit],
|
||||
soft_obs_snapshot_limit: values[:soft_obs_snapshot_limit],
|
||||
soft_obs_general_limit: values[:soft_obs_general_limit],
|
||||
soft_twitter_datasource_limit: values[:soft_twitter_datasource_limit]
|
||||
}
|
||||
end
|
||||
|
||||
describe '#create' do
|
||||
after(:each) do
|
||||
@user.destroy if @user
|
||||
end
|
||||
|
||||
it 'owner cannot enable soft limits if he has not' do
|
||||
old_limits = update_soft_limits(@org_user_owner, false)
|
||||
|
||||
post create_organization_user_url(user_domain: @org_user_owner.username),
|
||||
user: user_params.merge(soft_limits_params("1")),
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.status.should eq 422
|
||||
|
||||
Carto::User.exists?(username: user_params[:username]).should be_false
|
||||
|
||||
update_soft_limits(@org_user_owner, old_limits)
|
||||
end
|
||||
|
||||
it 'owner cannot enable geocoding limit if he has not' do
|
||||
old_limits = update_soft_limits(@org_user_owner, false)
|
||||
|
||||
post create_organization_user_url(user_domain: @org_user_owner.username),
|
||||
user: user_params.merge(soft_geocoding_limit: "1"),
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.status.should eq 422
|
||||
|
||||
Carto::User.exists?(username: user_params[:username]).should be_false
|
||||
|
||||
update_soft_limits(@org_user_owner, old_limits)
|
||||
end
|
||||
|
||||
# This test is needed now that soft limits toggles become disabled if owner can't assign
|
||||
it 'by default soft limits are disabled' do
|
||||
old_limits = update_soft_limits(@org_user_owner, false)
|
||||
|
||||
post create_organization_user_url(user_domain: @org_user_owner.username),
|
||||
user: user_params,
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.status.should eq 302
|
||||
|
||||
@user = Carto::User.where(username: user_params[:username]).first
|
||||
check_soft_limits(@user, false)
|
||||
|
||||
update_soft_limits(@org_user_owner, old_limits)
|
||||
end
|
||||
|
||||
it 'owner can enable soft limits if he has' do
|
||||
old_limits = update_soft_limits(@org_user_owner, true)
|
||||
|
||||
post create_organization_user_url(user_domain: @org_user_owner.username),
|
||||
user: user_params.merge(soft_limits_params("1")),
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.status.should eq 302
|
||||
|
||||
@user = User.where(username: user_params[:username]).first
|
||||
check_soft_limits(@user, true)
|
||||
|
||||
update_soft_limits(@org_user_owner, old_limits)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'update' do
|
||||
after(:each) do
|
||||
::User[@existing_user.id].destroy if @existing_user
|
||||
end
|
||||
|
||||
it 'owner cannot enable soft limits if he has not' do
|
||||
old_limits = update_soft_limits(@org_user_owner, false)
|
||||
check_soft_limits(@carto_org_user_owner, false)
|
||||
@existing_user = FactoryGirl.create(:carto_user,
|
||||
soft_limits_params(false).merge(organization: @carto_organization))
|
||||
|
||||
put update_organization_user_url(user_domain: @org_user_owner.username, id: @existing_user.username),
|
||||
user: soft_limits_params("1"),
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.status.should eq 422
|
||||
|
||||
@existing_user.reload
|
||||
check_soft_limits(@existing_user, false)
|
||||
update_soft_limits(@org_user_owner, old_limits)
|
||||
end
|
||||
|
||||
it 'owner can enable soft limits if he has' do
|
||||
old_limits = update_soft_limits(@org_user_owner, true)
|
||||
@existing_user = FactoryGirl.create(:carto_user,
|
||||
soft_limits_params(false).merge(organization: @carto_organization))
|
||||
|
||||
put update_organization_user_url(user_domain: @org_user_owner.username, id: @existing_user.username),
|
||||
user: soft_limits_params("1"),
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.status.should eq 302
|
||||
|
||||
@existing_user.reload
|
||||
check_soft_limits(@existing_user, true)
|
||||
update_soft_limits(@org_user_owner, old_limits)
|
||||
end
|
||||
|
||||
it 'owner can disable soft limits if he has' do
|
||||
old_limits = update_soft_limits(@org_user_owner, true)
|
||||
@existing_user = FactoryGirl.create(:carto_user,
|
||||
soft_limits_params(true).merge(organization: @carto_organization))
|
||||
put update_organization_user_url(user_domain: @org_user_owner.username, id: @existing_user.username),
|
||||
user: soft_limits_params("0"),
|
||||
password_confirmation: @org_user_owner.password
|
||||
last_response.status.should eq 302
|
||||
|
||||
@existing_user.reload
|
||||
check_soft_limits(@existing_user, false)
|
||||
update_soft_limits(@org_user_owner, old_limits)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,454 @@
|
||||
require_relative '../../spec_helper_min'
|
||||
require_relative '../../factories/organizations_contexts'
|
||||
|
||||
describe Admin::OrganizationsController do
|
||||
include Warden::Test::Helpers
|
||||
include_context 'organization with users helper'
|
||||
|
||||
let(:out_of_quota_message) { "Your organization has run out of quota" }
|
||||
let(:out_of_seats_message) { "Your organization has run out of seats" }
|
||||
|
||||
before(:all) do
|
||||
@org_user_2.org_admin = true
|
||||
@org_user_2.save
|
||||
end
|
||||
|
||||
describe '#settings' do
|
||||
let(:payload) do
|
||||
{
|
||||
organization: { color: '#ff0000' }
|
||||
}
|
||||
end
|
||||
|
||||
let(:payload_password) do
|
||||
{
|
||||
organization: { color: '#ff0000' },
|
||||
password_confirmation: @org_user_owner.password
|
||||
}
|
||||
end
|
||||
|
||||
let(:payload_wrong_password) do
|
||||
{
|
||||
organization: { color: '#ff0000' },
|
||||
password_confirmation: 'prapra'
|
||||
}
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
host! "#{@organization.name}.localhost.lan"
|
||||
Organization.any_instance.stubs(:update_in_central).returns(true)
|
||||
end
|
||||
|
||||
it 'cannot be accessed by non owner users' do
|
||||
login_as(@org_user_1, scope: @org_user_1.username)
|
||||
get organization_settings_url(user_domain: @org_user_1.username)
|
||||
response.status.should eq 404
|
||||
|
||||
login_as(@org_user_2, scope: @org_user_2.username)
|
||||
get organization_settings_url(user_domain: @org_user_2.username)
|
||||
response.status.should eq 404
|
||||
end
|
||||
|
||||
it 'cannot be updated by non owner users' do
|
||||
login_as(@org_user_1, scope: @org_user_1.username)
|
||||
put organization_settings_update_url(user_domain: @org_user_1.username), payload
|
||||
response.status.should eq 404
|
||||
|
||||
login_as(@org_user_2, scope: @org_user_2.username)
|
||||
put organization_settings_update_url(user_domain: @org_user_2.username), payload
|
||||
response.status.should eq 404
|
||||
end
|
||||
|
||||
it 'can be accessed by owner user' do
|
||||
login_as(@org_user_owner, scope: @org_user_owner.username)
|
||||
get organization_settings_url(user_domain: @org_user_owner.username)
|
||||
response.status.should eq 200
|
||||
end
|
||||
|
||||
it 'can be updated by owner user' do
|
||||
login_as(@org_user_owner, scope: @org_user_owner.username)
|
||||
put organization_settings_update_url(user_domain: @org_user_owner.username), payload_password
|
||||
response.status.should eq 302
|
||||
end
|
||||
|
||||
it 'fails to update if no password_confirmation' do
|
||||
login_as(@org_user_owner, scope: @org_user_owner.username)
|
||||
put organization_settings_update_url(user_domain: @org_user_owner.username), payload
|
||||
response.status.should eq 403
|
||||
response.body.should match /Confirmation password sent does not match your current password/
|
||||
end
|
||||
|
||||
it 'fails to update if wrong password_confirmation' do
|
||||
login_as(@org_user_owner, scope: @org_user_owner.username)
|
||||
put organization_settings_update_url(user_domain: @org_user_owner.username), payload_wrong_password
|
||||
response.status.should eq 403
|
||||
response.body.should match /Confirmation password sent does not match your current password/
|
||||
end
|
||||
end
|
||||
|
||||
describe '#regenerate_api_keys' do
|
||||
it 'regenerate api keys for all org users' do
|
||||
api_key = @carto_org_user_owner.api_keys.create_regular_key!(name: 'wadus', grants: [{ type: 'apis', apis: [] }])
|
||||
@organization.engine_enabled = true
|
||||
@organization.save
|
||||
host! "#{@organization.name}.localhost.lan"
|
||||
login_as(@org_user_owner, scope: @org_user_owner.username)
|
||||
post regenerate_organization_users_api_key_url(
|
||||
user_domain: @org_user_owner.username,
|
||||
password_confirmation: @org_user_owner.password
|
||||
)
|
||||
response.status.should eq 302
|
||||
|
||||
@organization.users.each do |u|
|
||||
old_api_key = u.api_key
|
||||
u.reload
|
||||
expect(u.api_key).to_not eq old_api_key
|
||||
end
|
||||
expect { api_key.reload }.to(change { api_key.token })
|
||||
api_key.destroy
|
||||
end
|
||||
end
|
||||
|
||||
describe '#delete' do
|
||||
before(:all) do
|
||||
@delete_org = test_organization
|
||||
@delete_org.save
|
||||
|
||||
helper = TestUserFactory.new
|
||||
@delete_org_owner = helper.create_owner(@delete_org)
|
||||
|
||||
@delete_org_user1 = @helper.create_test_user(unique_name('user'), @delete_org)
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
@delete_org.destroy_cascade if Carto::Organization.exists?(@delete_org.id)
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
host! "#{@delete_org.name}.localhost.lan"
|
||||
Organization.any_instance.stubs(:update_in_central).returns(true)
|
||||
end
|
||||
|
||||
it 'cannot be accessed by non owner users' do
|
||||
login_as(@delete_org_user1, scope: @delete_org_user1.username)
|
||||
delete organization_destroy_url(user_domain: @delete_org_user1.username)
|
||||
response.status.should eq 404
|
||||
end
|
||||
|
||||
describe 'as owner' do
|
||||
before(:each) do
|
||||
login_as(@delete_org_owner, scope: @delete_org_owner.username)
|
||||
end
|
||||
|
||||
it 'returns 400 if no password confirmation is provided' do
|
||||
delete organization_destroy_url(user_domain: @delete_org_owner.username)
|
||||
response.status.should eq 400
|
||||
response.body.should include("Password doesn't match")
|
||||
end
|
||||
|
||||
it 'returns 400 if password confirmation is wrong' do
|
||||
payload = { deletion_password_confirmation: @delete_org_owner.password + 'wadus' }
|
||||
delete organization_destroy_url(user_domain: @delete_org_owner.username), payload
|
||||
response.status.should eq 400
|
||||
end
|
||||
|
||||
it 'deletes organization and redirects if passwords match' do
|
||||
payload = { deletion_password_confirmation: @delete_org_owner.password }
|
||||
delete organization_destroy_url(user_domain: @delete_org_owner.username), payload
|
||||
response.status.should eq 302
|
||||
Carto::Organization.exists?(@delete_org.id).should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#auth' do
|
||||
let(:payload) do
|
||||
{
|
||||
organization: {
|
||||
whitelisted_email_domains: '',
|
||||
auth_username_password_enabled: true,
|
||||
auth_google_enabled: true,
|
||||
auth_github_enabled: true,
|
||||
strong_passwords_enabled: false,
|
||||
password_expiration_in_d: 1
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
let(:payload_password) do
|
||||
{
|
||||
organization: {
|
||||
whitelisted_email_domains: '',
|
||||
auth_username_password_enabled: true,
|
||||
auth_google_enabled: true,
|
||||
auth_github_enabled: true,
|
||||
strong_passwords_enabled: false,
|
||||
password_expiration_in_d: 1
|
||||
},
|
||||
password_confirmation: @org_user_owner.password
|
||||
}
|
||||
end
|
||||
|
||||
let(:payload_wrong_password) do
|
||||
{
|
||||
organization: {
|
||||
whitelisted_email_domains: '',
|
||||
auth_username_password_enabled: true,
|
||||
auth_google_enabled: true,
|
||||
auth_github_enabled: true,
|
||||
strong_passwords_enabled: false
|
||||
},
|
||||
password_confirmation: 'prapra'
|
||||
}
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
host! "#{@organization.name}.localhost.lan"
|
||||
login_as(@org_user_owner, scope: @org_user_owner.username)
|
||||
Organization.any_instance.stubs(:update_in_central).returns(true)
|
||||
end
|
||||
|
||||
it 'cannot be accessed by non owner users' do
|
||||
login_as(@org_user_1, scope: @org_user_1.username)
|
||||
get organization_auth_url(user_domain: @org_user_1.username)
|
||||
response.status.should eq 404
|
||||
|
||||
login_as(@org_user_2, scope: @org_user_2.username)
|
||||
get organization_auth_url(user_domain: @org_user_2.username)
|
||||
response.status.should eq 404
|
||||
end
|
||||
|
||||
it 'cannot be updated by non owner users' do
|
||||
login_as(@org_user_1, scope: @org_user_1.username)
|
||||
put organization_auth_update_url(user_domain: @org_user_1.username), payload
|
||||
response.status.should eq 404
|
||||
|
||||
login_as(@org_user_2, scope: @org_user_2.username)
|
||||
put organization_auth_update_url(user_domain: @org_user_2.username), payload
|
||||
response.status.should eq 404
|
||||
end
|
||||
|
||||
it 'can be accessed by owner user' do
|
||||
login_as(@org_user_owner, scope: @org_user_owner.username)
|
||||
get organization_auth_url(user_domain: @org_user_owner.username)
|
||||
response.status.should eq 200
|
||||
end
|
||||
|
||||
it 'can be updated by owner user' do
|
||||
login_as(@org_user_owner, scope: @org_user_owner.username)
|
||||
put organization_auth_update_url(user_domain: @org_user_owner.username), payload_password
|
||||
response.status.should eq 302
|
||||
end
|
||||
|
||||
it 'cannot be updated by owner user if missing password_confirmation' do
|
||||
login_as(@org_user_owner, scope: @org_user_owner.username)
|
||||
put organization_auth_update_url(user_domain: @org_user_owner.username), payload
|
||||
response.status.should eq 403
|
||||
response.body.should match /Confirmation password sent does not match your current password/
|
||||
end
|
||||
|
||||
it 'cannot be updated by owner user if wrong password_confirmation' do
|
||||
login_as(@org_user_owner, scope: @org_user_owner.username)
|
||||
put organization_auth_update_url(user_domain: @org_user_owner.username), payload_wrong_password
|
||||
response.status.should eq 403
|
||||
response.body.should match /Confirmation password sent does not match your current password/
|
||||
end
|
||||
|
||||
it 'updates password_expiration_in_d' do
|
||||
@organization.password_expiration_in_d = nil
|
||||
@organization.save
|
||||
|
||||
login_as(@org_user_owner, scope: @org_user_owner.username)
|
||||
put organization_auth_update_url(user_domain: @org_user_owner.username), payload_password
|
||||
response.status.should eq 302
|
||||
@organization.reload
|
||||
@organization.password_expiration_in_d.should eq 1
|
||||
|
||||
payload_password[:organization][:password_expiration_in_d] = ''
|
||||
host! "#{@organization.name}.localhost.lan"
|
||||
login_as(@org_user_owner, scope: @org_user_owner.username)
|
||||
put organization_auth_update_url(user_domain: @org_user_owner.username), payload_password
|
||||
response.status.should eq 302
|
||||
@organization.reload
|
||||
@organization.password_expiration_in_d.should be_nil
|
||||
end
|
||||
|
||||
describe 'signup enabled' do
|
||||
before(:all) do
|
||||
@organization.whitelisted_email_domains = ['carto.com']
|
||||
@organization.save
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
@organization.signup_page_enabled.should eq true
|
||||
end
|
||||
|
||||
it 'does not display out warning messages if organization signup would work' do
|
||||
@organization.unassigned_quota.should > @organization.default_quota_in_bytes
|
||||
|
||||
get organization_auth_url(user_domain: @org_user_owner.username)
|
||||
|
||||
response.status.should eq 200
|
||||
response.body.should_not include(out_of_quota_message)
|
||||
response.body.should_not include(out_of_seats_message)
|
||||
end
|
||||
|
||||
it 'displays out of quota message if there is no remaining quota' do
|
||||
old_quota_in_bytes = @organization.quota_in_bytes
|
||||
|
||||
old_remaining_quota = @organization.unassigned_quota
|
||||
new_quota = (@organization.quota_in_bytes - old_remaining_quota) + (@organization.default_quota_in_bytes / 2)
|
||||
@organization.reload
|
||||
@org_user_owner.reload
|
||||
@organization.quota_in_bytes = new_quota
|
||||
@organization.save
|
||||
|
||||
get organization_auth_url(user_domain: @org_user_owner.username)
|
||||
|
||||
response.status.should eq 200
|
||||
response.body.should include(out_of_quota_message)
|
||||
|
||||
@organization.quota_in_bytes = old_quota_in_bytes
|
||||
@organization.save
|
||||
end
|
||||
|
||||
it 'displays out of seats message if there are no seats left' do
|
||||
old_seats = @organization.seats
|
||||
|
||||
new_seats = @organization.seats - @organization.remaining_seats
|
||||
@organization.reload
|
||||
@org_user_owner.reload
|
||||
@organization.seats = new_seats
|
||||
@organization.save
|
||||
|
||||
get organization_auth_url(user_domain: @org_user_owner.username)
|
||||
|
||||
response.status.should eq 200
|
||||
response.body.should include(out_of_seats_message)
|
||||
|
||||
@organization.seats = old_seats
|
||||
@organization.save
|
||||
end
|
||||
end
|
||||
|
||||
describe 'signup disabled' do
|
||||
before(:all) do
|
||||
@organization.whitelisted_email_domains = []
|
||||
@organization.save
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
@organization.signup_page_enabled.should eq false
|
||||
end
|
||||
|
||||
it 'does not display out warning messages even without quota and seats' do
|
||||
old_quota_in_bytes = @organization.quota_in_bytes
|
||||
old_seats = @organization.seats
|
||||
|
||||
@organization.reload
|
||||
@org_user_owner.reload
|
||||
|
||||
@organization.seats = @organization.assigned_seats
|
||||
@organization.quota_in_bytes = @organization.assigned_quota + 1
|
||||
@organization.save
|
||||
|
||||
get organization_auth_url(user_domain: @org_user_owner.username)
|
||||
|
||||
response.status.should eq 200
|
||||
response.body.should_not include(out_of_quota_message)
|
||||
response.body.should_not include(out_of_seats_message)
|
||||
|
||||
@organization.quota_in_bytes = old_quota_in_bytes
|
||||
@organization.seats = old_seats
|
||||
@organization.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples_for 'notifications' do
|
||||
before(:each) do
|
||||
host! "#{@organization.name}.localhost.lan"
|
||||
login_as(@admin_user, scope: @admin_user.username)
|
||||
end
|
||||
|
||||
describe '#notifications' do
|
||||
it 'displays last notification' do
|
||||
body = 'Free meal today'
|
||||
FactoryGirl.create(:notification, organization: @carto_organization, body: body)
|
||||
get organization_notifications_admin_url(user_domain: @admin_user.username)
|
||||
response.status.should eq 200
|
||||
response.body.should include(body)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#new_notification' do
|
||||
it 'creates a new notification' do
|
||||
params = {
|
||||
body: 'the body',
|
||||
recipients: Carto::Notification::RECIPIENT_ALL
|
||||
}
|
||||
post new_organization_notification_admin_url(
|
||||
user_domain: @admin_user.username
|
||||
), carto_notification: params, password_confirmation: @admin_user.password
|
||||
response.status.should eq 302
|
||||
flash[:success].should eq 'Notification sent!'
|
||||
notification = @carto_organization.reload.notifications.first
|
||||
notification.body.should eq params[:body]
|
||||
notification.recipients.should eq params[:recipients]
|
||||
notification.icon.should eq Carto::Notification::ICON_ALERT
|
||||
end
|
||||
|
||||
it 'does not create a new notification if wrong password_confirmation' do
|
||||
params = {
|
||||
body: 'the body wrong',
|
||||
recipients: Carto::Notification::RECIPIENT_ALL
|
||||
}
|
||||
post new_organization_notification_admin_url(
|
||||
user_domain: @admin_user.username
|
||||
), carto_notification: params, password_confirmation: 'prapra'
|
||||
response.status.should eq 403
|
||||
response.body.should match /Confirmation password sent does not match your current password/
|
||||
notification = @carto_organization.reload.notifications.first
|
||||
notification.body.should_not eq params[:body]
|
||||
end
|
||||
|
||||
it 'does not create a new notification if missing password_confirmation' do
|
||||
params = {
|
||||
body: 'the body missing',
|
||||
recipients: Carto::Notification::RECIPIENT_ALL
|
||||
}
|
||||
post new_organization_notification_admin_url(user_domain: @admin_user.username), carto_notification: params
|
||||
response.status.should eq 403
|
||||
response.body.should match /Confirmation password sent does not match your current password/
|
||||
notification = @carto_organization.reload.notifications.first
|
||||
notification.body.should_not eq params[:body]
|
||||
end
|
||||
end
|
||||
|
||||
describe '#destroy_notification' do
|
||||
it 'destroys a notification' do
|
||||
notification = @carto_organization.notifications.first
|
||||
delete destroy_organization_notification_admin_url(user_domain: @admin_user.username, id: notification.id)
|
||||
response.status.should eq 302
|
||||
flash[:success].should eq 'Notification was successfully deleted!'
|
||||
@carto_organization.reload.notifications.should_not include(notification)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with organization owner' do
|
||||
it_behaves_like 'notifications' do
|
||||
before(:all) do
|
||||
@admin_user = @org_user_owner
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with organization admin' do
|
||||
it_behaves_like 'notifications' do
|
||||
before(:all) do
|
||||
@admin_user = @org_user_2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,363 @@
|
||||
require_relative '../../spec_helper'
|
||||
require_relative '../../../app/controllers/admin/pages_controller'
|
||||
require_relative '../../factories/organizations_contexts'
|
||||
require_relative '../../factories/carto_visualizations'
|
||||
|
||||
def app
|
||||
CartoDB::Application.new
|
||||
end #app
|
||||
|
||||
describe Admin::PagesController do
|
||||
include Rack::Test::Methods
|
||||
include Warden::Test::Helpers
|
||||
|
||||
JSON_HEADER = {'CONTENT_TYPE' => 'application/json'}
|
||||
|
||||
before(:all) do
|
||||
|
||||
@non_org_user_name = 'development'
|
||||
|
||||
@org_name = 'foobar'
|
||||
@org_user_name = 'foo'
|
||||
|
||||
@other_org_user_name = 'other'
|
||||
|
||||
@belongs_to_org = true
|
||||
@user_org = true
|
||||
end
|
||||
|
||||
describe '#index' do
|
||||
before(:each) do
|
||||
host! "#{@org_name}.localhost.lan"
|
||||
end
|
||||
|
||||
it 'returns 404 if user does not belongs to host organization' do
|
||||
user = prepare_user(@non_org_user_name)
|
||||
|
||||
get "/u/#{@non_org_user_name}", {}, JSON_HEADER
|
||||
|
||||
last_response.status.should == 404
|
||||
|
||||
user.delete
|
||||
end
|
||||
|
||||
it 'returns 200 if it is an org user and belongs to host organization' do
|
||||
user = prepare_user(@org_user_name, @user_org, @belongs_to_org)
|
||||
|
||||
get "/u/#{@org_user_name}", {}, JSON_HEADER
|
||||
|
||||
last_response.status.should == 200
|
||||
|
||||
user.delete
|
||||
end
|
||||
|
||||
it 'redirects_to dashboard if organization user is logged in' do
|
||||
user = prepare_user(@org_user_name, @user_org, @belongs_to_org)
|
||||
|
||||
login_as(user, scope: user.username)
|
||||
|
||||
get "/u/#{@org_user_name}", {}, JSON_HEADER
|
||||
|
||||
last_response.status.should == 302
|
||||
|
||||
follow_redirect!
|
||||
|
||||
uri = URI.parse(last_request.url)
|
||||
uri.host.should == "#{@org_name}.localhost.lan"
|
||||
uri.path.should == "/u/#{@org_user_name}/dashboard"
|
||||
|
||||
user.delete
|
||||
end
|
||||
|
||||
it 'redirects if it is an org user but gets called without organization' do
|
||||
user = prepare_user(@org_user_name, @user_org, @belongs_to_org)
|
||||
|
||||
host! "#{@org_user_name}.localhost.lan"
|
||||
get "", {}, JSON_HEADER
|
||||
|
||||
last_response.status.should == 302
|
||||
follow_redirect!
|
||||
last_response.status.should == 200
|
||||
|
||||
user.delete
|
||||
end
|
||||
|
||||
it 'returns 404 if it is an org user but does NOT belong to host organization' do
|
||||
user = prepare_user(@other_org_user_name, @user_org, !@belongs_to_org)
|
||||
|
||||
get "/u/#{@other_org_user_name}", {}, JSON_HEADER
|
||||
|
||||
last_response.status.should == 404
|
||||
|
||||
user.delete
|
||||
end
|
||||
|
||||
it 'returns 404 if user does NOT exist' do
|
||||
get '/u/non-exitant-user', {}, JSON_HEADER
|
||||
|
||||
last_response.status.should == 404
|
||||
end
|
||||
|
||||
it 'redirects to user feed home if current user and current viewer are different' do
|
||||
anyuser = prepare_user('anyuser')
|
||||
anyviewer = prepare_user('anyviewer')
|
||||
login_as(anyviewer, scope: anyviewer.username)
|
||||
host! "#{anyuser.username}.localhost.lan"
|
||||
|
||||
get '', {}, JSON_HEADER
|
||||
|
||||
last_response.status.should == 302
|
||||
follow_redirect!
|
||||
last_response.status.should == 200
|
||||
uri = URI.parse(last_request.url)
|
||||
uri.host.should == 'anyuser.localhost.lan'
|
||||
uri.path.should == '/me'
|
||||
|
||||
[anyuser, anyviewer].each(&:delete)
|
||||
end
|
||||
|
||||
it 'redirects to user feed if not logged in' do
|
||||
user = prepare_user('anyuser')
|
||||
host! 'anyuser.localhost.lan'
|
||||
|
||||
get '', {}, JSON_HEADER
|
||||
|
||||
last_response.status.should == 302
|
||||
uri = URI.parse(last_response.location)
|
||||
uri.host.should == 'anyuser.localhost.lan'
|
||||
uri.path.should == '/me'
|
||||
follow_redirect!
|
||||
last_response.status.should == 200
|
||||
|
||||
user.delete
|
||||
end
|
||||
|
||||
it 'redirects to local login page if no user is specified and Central is not enabled' do
|
||||
Cartodb.with_config(cartodb_central_api: {}) do
|
||||
user = prepare_user('anyuser')
|
||||
host! 'localhost.lan'
|
||||
CartoDB.stubs(:session_domain).returns('localhost.lan')
|
||||
CartoDB.stubs(:subdomainless_urls?).returns(true)
|
||||
|
||||
get '', {}, JSON_HEADER
|
||||
|
||||
last_response.status.should == 302
|
||||
uri = URI.parse(last_response.location)
|
||||
uri.host.should == 'localhost.lan'
|
||||
uri.path.should == '/login'
|
||||
follow_redirect!
|
||||
last_response.status.should == 200
|
||||
|
||||
user.delete
|
||||
end
|
||||
end
|
||||
|
||||
it 'redirects to Central login page if no user is specified and Central is enabled' do
|
||||
central_host = 'somewhere.lan'
|
||||
central_port = 4321
|
||||
Cartodb.with_config(
|
||||
cartodb_central_api: {
|
||||
'host' => central_host,
|
||||
'port' => central_port,
|
||||
'username' => 'api',
|
||||
'password' => 'test'
|
||||
}
|
||||
) do
|
||||
user = prepare_user('anyuser')
|
||||
host! 'localhost.lan'
|
||||
CartoDB.stubs(:session_domain).returns('localhost.lan')
|
||||
CartoDB.stubs(:subdomainless_urls?).returns(true)
|
||||
|
||||
get '', {}, JSON_HEADER
|
||||
|
||||
last_response.status.should == 302
|
||||
uri = URI.parse(last_response.location)
|
||||
uri.host.should == 'localhost.lan'
|
||||
uri.path.should == '/login'
|
||||
follow_redirect!
|
||||
|
||||
last_response.status.should == 302
|
||||
uri = URI.parse(last_response.location)
|
||||
uri.host.should == central_host
|
||||
uri.port.should == central_port
|
||||
uri.path.should == '/login'
|
||||
follow_redirect!
|
||||
|
||||
user.delete
|
||||
end
|
||||
end
|
||||
|
||||
it 'redirects and loads the dashboard if the user is logged in' do
|
||||
anyuser = prepare_user('anyuser')
|
||||
host! 'localhost.lan'
|
||||
login_as(anyuser, scope: anyuser.username)
|
||||
CartoDB.stubs(:session_domain).returns('localhost.lan')
|
||||
CartoDB.stubs(:subdomainless_urls?).returns(true)
|
||||
|
||||
get '', {}, JSON_HEADER
|
||||
|
||||
last_response.status.should == 302
|
||||
uri = URI.parse(last_response.location)
|
||||
uri.host.should == 'localhost.lan'
|
||||
uri.path.should == '/user/anyuser/dashboard'
|
||||
|
||||
anyuser.delete
|
||||
end
|
||||
|
||||
it 'extracts username from redirection for dashboard with subdomainless' do
|
||||
# we use this to avoid generating the static assets in CI
|
||||
Admin::VisualizationsController.any_instance.stubs(:render).returns('')
|
||||
|
||||
username = 'endedwithu'
|
||||
anyuser = prepare_user(username)
|
||||
host! 'localhost.lan'
|
||||
login_as(anyuser, scope: anyuser.username)
|
||||
CartoDB.stubs(:session_domain).returns('localhost.lan')
|
||||
CartoDB.stubs(:subdomainless_urls?).returns(true)
|
||||
|
||||
get '', {}, JSON_HEADER
|
||||
|
||||
last_response.status.should == 302
|
||||
uri = URI.parse(last_response.location)
|
||||
uri.host.should == 'localhost.lan'
|
||||
uri.path.should == "/user/#{username}/dashboard"
|
||||
|
||||
login_as(anyuser, scope: anyuser.username)
|
||||
location = last_response.location
|
||||
User.any_instance.stubs(:db_size_in_bytes).returns(0)
|
||||
get location
|
||||
last_response.status.should == 200
|
||||
|
||||
anyuser.delete
|
||||
end
|
||||
|
||||
it 'redirects to login without login' do
|
||||
host! 'localhost.lan'
|
||||
|
||||
get '', {}, JSON_HEADER
|
||||
|
||||
uri = URI.parse(last_response.location)
|
||||
uri.host.should == 'localhost.lan'
|
||||
uri.path.should == "/login"
|
||||
end
|
||||
end
|
||||
|
||||
describe '#sitemap' do
|
||||
include Carto::Factories::Visualizations
|
||||
it 'should return 404 if no user or organization is provided' do
|
||||
get '/sitemap.xml'
|
||||
last_response.status.should == 404
|
||||
end
|
||||
|
||||
describe 'for organizations' do
|
||||
include_context 'organization with users helper'
|
||||
|
||||
before(:each) do
|
||||
host! "#{@carto_organization.name}.localhost.lan:#{Cartodb.config[:http_port]}"
|
||||
end
|
||||
|
||||
it 'returns an empty body if there are not visualizations' do
|
||||
get public_sitemap_url(user_domain: @carto_organization.name)
|
||||
document = Nokogiri::XML(last_response.body)
|
||||
document.child.child.text.should eq "\n"
|
||||
end
|
||||
|
||||
it 'returns public and published visualizations' do
|
||||
private_attrs = { privacy: Carto::Visualization::PRIVACY_PRIVATE }
|
||||
create_full_visualization(@carto_org_user_1, visualization_attributes: private_attrs)
|
||||
unpublished_attrs = { privacy: Carto::Visualization::PRIVACY_PUBLIC, version: 3 }
|
||||
create_full_visualization(@carto_org_user_1, visualization_attributes: unpublished_attrs)
|
||||
public_attrs = { privacy: Carto::Visualization::PRIVACY_PUBLIC }
|
||||
_, _, _, visualization = create_full_visualization(@carto_org_user_1, visualization_attributes: public_attrs)
|
||||
get public_sitemap_url(user_domain: @carto_organization.name)
|
||||
last_response.status.should eq 200
|
||||
document = Nokogiri::XML(last_response.body)
|
||||
url_and_dates = document.search('url').map { |url| [url.at('loc').text, url.at('lastmod').text] }
|
||||
url_and_dates.count.should eq 1
|
||||
|
||||
url1 = public_visualizations_public_map_url(user_domain: @carto_org_user_1.username, id: visualization.id)
|
||||
url_and_dates.map { |url_and_date| url_and_date[0] }.should eq [url1.gsub(/\/user\/[^\/]*\//, '/')]
|
||||
end
|
||||
end
|
||||
|
||||
describe 'for users' do
|
||||
include_context 'users helper'
|
||||
|
||||
before(:each) do
|
||||
host! "#{@carto_user1.username}.localhost.lan:#{Cartodb.config[:http_port]}"
|
||||
end
|
||||
|
||||
it 'returns public and published visualizations' do
|
||||
private_attrs = { privacy: Carto::Visualization::PRIVACY_PRIVATE }
|
||||
create_full_visualization(@carto_user1, visualization_attributes: private_attrs)
|
||||
unpublished_attrs = { privacy: Carto::Visualization::PRIVACY_PUBLIC, version: 3 }
|
||||
create_full_visualization(@carto_user1, visualization_attributes: unpublished_attrs)
|
||||
public_attrs = { privacy: Carto::Visualization::PRIVACY_PUBLIC }
|
||||
_, _, _, visualization = create_full_visualization(@carto_user1, visualization_attributes: public_attrs)
|
||||
get public_sitemap_url(user_domain: @carto_user1.username)
|
||||
last_response.status.should eq 200
|
||||
document = Nokogiri::XML(last_response.body)
|
||||
url_and_dates = document.search('url').map { |url| [url.at('loc').text, url.at('lastmod').text] }
|
||||
url_and_dates.count.should eq 1
|
||||
|
||||
url1 = public_visualizations_public_map_url(id: visualization.id)
|
||||
url_and_dates.map { |url_and_date| url_and_date[0] }.should eq [url1.gsub(/\/user\/[^\/]*\//, '/')]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#datasets' do
|
||||
include_context 'users helper'
|
||||
|
||||
before(:each) do
|
||||
host! "#{@carto_user1.username}.localhost.lan:#{Cartodb.config[:http_port]}"
|
||||
end
|
||||
|
||||
it 'returns 200 if a dataset has no table' do
|
||||
FactoryGirl.create(:table_visualization, user_id: @carto_user1.id, privacy: Carto::Visualization::PRIVACY_PUBLIC)
|
||||
Carto::Visualization.count.should eql 1
|
||||
visualization = Carto::Visualization.first
|
||||
visualization.table.should be_nil
|
||||
|
||||
get public_datasets_home_url(user_domain: @carto_user1.username)
|
||||
|
||||
last_response.status.should == 200
|
||||
last_response.body.should =~ /doesn\'t have any items/
|
||||
end
|
||||
end
|
||||
|
||||
def mock_explore_feature_flag
|
||||
anyuser = prepare_user('anyuser')
|
||||
::User.any_instance.stubs(:has_feature_flag?)
|
||||
.with('explore_site')
|
||||
.returns(true)
|
||||
::User.stubs(:where).returns(anyuser)
|
||||
anyuser.stubs(:first).returns(anyuser)
|
||||
anyuser
|
||||
end
|
||||
|
||||
def prepare_user(user_name, org_user=false, belongs_to_org=false)
|
||||
user = create_user(
|
||||
username: user_name,
|
||||
email: "#{user_name}@example.com",
|
||||
password: 'longer_than_MIN_PASSWORD_LENGTH',
|
||||
fake_user: true,
|
||||
quota_in_bytes: 10000000
|
||||
)
|
||||
|
||||
user.stubs(:username => user_name, :organization_user? => org_user)
|
||||
|
||||
if org_user
|
||||
org = mock
|
||||
org.stubs(name: @org_name)
|
||||
org.stubs(password_expiration_in_d: nil)
|
||||
user.stubs(organization: org)
|
||||
Organization.stubs(:where).with(name: @org_name).returns([org])
|
||||
Organization.stubs(:where).with(name: @org_user_name).returns([org])
|
||||
::User.any_instance.stubs(:belongs_to_organization?).with(org).returns(belongs_to_org)
|
||||
end
|
||||
|
||||
user
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,70 @@
|
||||
require 'sequel'
|
||||
require 'rack/test'
|
||||
require 'json'
|
||||
require_relative '../../spec_helper'
|
||||
|
||||
def app
|
||||
CartoDB::Application.new
|
||||
end #app
|
||||
|
||||
describe Admin::TablesController do
|
||||
include Rack::Test::Methods
|
||||
include Warden::Test::Helpers
|
||||
|
||||
before(:all) do
|
||||
CartoDB::Varnish.any_instance.stubs(:send_command).returns(true)
|
||||
|
||||
@user = FactoryGirl.create(:valid_user)
|
||||
|
||||
@api_key = @user.api_key
|
||||
@user.stubs(:should_load_common_data?).returns(false)
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
@user.destroy
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
bypass_named_maps
|
||||
CartoDB::Varnish.any_instance.stubs(:send_command).returns(true)
|
||||
@db = SequelRails.connection
|
||||
delete_user_data @user
|
||||
@headers = {
|
||||
'CONTENT_TYPE' => 'application/json',
|
||||
}
|
||||
host! "#{@user.username}.localhost.lan"
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
bypass_named_maps
|
||||
delete_user_data(@user)
|
||||
@user.destroy
|
||||
end
|
||||
|
||||
describe 'GET /dashboard' do
|
||||
it 'returns a list of tables' do
|
||||
# we use this to avoid generating the static assets in CI
|
||||
Admin::VisualizationsController.any_instance.stubs(:render).returns('')
|
||||
|
||||
login_as(@user, scope: @user.username)
|
||||
|
||||
get "/dashboard", {}, @headers
|
||||
last_response.status.should == 200
|
||||
end
|
||||
end # GET /tables
|
||||
|
||||
describe 'GET /tables/:id' do
|
||||
it 'returns a table' do
|
||||
id = factory.id
|
||||
login_as(@user, scope: @user.username)
|
||||
|
||||
get "/tables/#{id}", {}, @headers
|
||||
last_response.status.should == 200
|
||||
end
|
||||
end # GET /tables/:id
|
||||
|
||||
def factory
|
||||
new_table(user_id: @user.id).save.reload
|
||||
end #table_attributes
|
||||
|
||||
end # Admin::TablesController
|
||||
@@ -0,0 +1,769 @@
|
||||
require 'sequel'
|
||||
require 'rack/test'
|
||||
require 'json'
|
||||
require_relative '../../spec_helper'
|
||||
require_relative '../../support/factories/organizations'
|
||||
require_relative '../../../app/controllers/admin/visualizations_controller'
|
||||
require 'helpers/unique_names_helper'
|
||||
|
||||
def app
|
||||
CartoDB::Application.new
|
||||
end #app
|
||||
|
||||
describe Admin::VisualizationsController do
|
||||
include UniqueNamesHelper
|
||||
include Rack::Test::Methods
|
||||
include Warden::Test::Helpers
|
||||
include CacheHelper
|
||||
include Carto::Factories::Visualizations
|
||||
|
||||
# Mock for a Rails context
|
||||
class ContextMock
|
||||
def initialize(global_context)
|
||||
@global_context = global_context
|
||||
end
|
||||
|
||||
def request
|
||||
nil
|
||||
end
|
||||
|
||||
def polymorphic_path(*args)
|
||||
@global_context.polymorphic_path(*args)
|
||||
end
|
||||
end
|
||||
|
||||
before(:all) do
|
||||
@user = FactoryGirl.create(:valid_user, private_tables_enabled: true)
|
||||
|
||||
@api_key = @user.api_key
|
||||
@user.stubs(:should_load_common_data?).returns(false)
|
||||
|
||||
@headers = {
|
||||
'CONTENT_TYPE' => 'application/json',
|
||||
}
|
||||
@mock_context = ContextMock.new(self)
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
@user.destroy
|
||||
end
|
||||
|
||||
before(:each) do
|
||||
bypass_named_maps
|
||||
delete_user_data @user
|
||||
host! "#{@user.username}.localhost.lan"
|
||||
end
|
||||
|
||||
describe 'GET /viz' do
|
||||
it 'returns a list of visualizations' do
|
||||
# we use this to avoid generating the static assets in CI
|
||||
Admin::VisualizationsController.any_instance.stubs(:render).returns('')
|
||||
login_as(@user, scope: @user.username)
|
||||
|
||||
get "/viz", {}, @headers
|
||||
last_response.status.should == 200
|
||||
end
|
||||
|
||||
it 'returns 403 if user not logged in' do
|
||||
get "/viz", {}, @headers
|
||||
last_response.status.should == 302
|
||||
end
|
||||
end # GET /viz
|
||||
|
||||
describe 'GET /viz:id' do
|
||||
it 'returns a visualization' do
|
||||
id = factory.fetch('id')
|
||||
login_as(@user, scope: @user.username)
|
||||
|
||||
get "/viz/#{id}", {}, @headers
|
||||
last_response.status.should == 200
|
||||
end
|
||||
|
||||
it 'redirects to the public view if visualization private' do
|
||||
id = factory.fetch('id')
|
||||
|
||||
get "/viz/#{id}", {}, @headers
|
||||
follow_redirect!
|
||||
last_request.path.should =~ %r{/viz/}
|
||||
end
|
||||
|
||||
it 'keeps the base path (table|visualization) when redirecting' do
|
||||
id = table_factory.id
|
||||
|
||||
get "/tables/#{id}", {}, @headers
|
||||
follow_redirect!
|
||||
last_request.path.should =~ %r{/tables/}
|
||||
end
|
||||
|
||||
describe 'redirects to builder' do
|
||||
describe 'for tables' do
|
||||
before(:each) do
|
||||
@id = table_factory.id
|
||||
end
|
||||
it 'if forced' do
|
||||
@user.stubs(:builder_enabled).returns(true)
|
||||
@user.stubs(:builder_enabled?).returns(true)
|
||||
|
||||
login_as(@user, scope: @user.username)
|
||||
get "/tables/#{@id}", {}, @headers
|
||||
last_response.status.should eq 302
|
||||
follow_redirect!
|
||||
last_request.path.should =~ %r{/dataset/}
|
||||
end
|
||||
|
||||
it 'only if enabled' do
|
||||
@user.stubs(:builder_enabled).returns(true)
|
||||
@user.stubs(:builder_enabled?).returns(false)
|
||||
|
||||
login_as(@user, scope: @user.username)
|
||||
get "/tables/#{@id}", {}, @headers
|
||||
last_response.status.should eq 200
|
||||
end
|
||||
|
||||
it 'only if forced' do
|
||||
@user.stubs(:builder_enabled).returns(nil)
|
||||
@user.stubs(:builder_enabled?).returns(false)
|
||||
|
||||
login_as(@user, scope: @user.username)
|
||||
get "/tables/#{@id}", {}, @headers
|
||||
last_response.status.should eq 200
|
||||
end
|
||||
end
|
||||
|
||||
describe 'for visualizations' do
|
||||
before(:each) do
|
||||
@id = factory.fetch('id')
|
||||
end
|
||||
it 'if forced' do
|
||||
@user.stubs(:builder_enabled).returns(true)
|
||||
@user.stubs(:builder_enabled?).returns(true)
|
||||
|
||||
login_as(@user, scope: @user.username)
|
||||
get "/viz/#{@id}", {}, @headers
|
||||
last_response.status.should eq 302
|
||||
follow_redirect!
|
||||
last_request.path.should =~ %r{/builder/}
|
||||
end
|
||||
|
||||
it 'only if enabled' do
|
||||
@user.stubs(:builder_enabled).returns(true)
|
||||
@user.stubs(:builder_enabled?).returns(false)
|
||||
|
||||
login_as(@user, scope: @user.username)
|
||||
get "/viz/#{@id}", {}, @headers
|
||||
last_response.status.should eq 200
|
||||
end
|
||||
|
||||
it 'only if forced' do
|
||||
@user.stubs(:builder_enabled).returns(nil)
|
||||
@user.stubs(:builder_enabled?).returns(false)
|
||||
|
||||
login_as(@user, scope: @user.username)
|
||||
get "/viz/#{@id}", {}, @headers
|
||||
last_response.status.should eq 200
|
||||
end
|
||||
|
||||
it 'never for vizjson2 visualizations' do
|
||||
@user.stubs(:builder_enabled).returns(true)
|
||||
@user.stubs(:builder_enabled?).returns(true)
|
||||
Carto::Visualization::any_instance.stubs(:uses_vizjson2?).returns(true)
|
||||
|
||||
login_as(@user, scope: @user.username)
|
||||
get public_visualizations_show_path(id: @id), {}, @headers
|
||||
last_response.status.should eq 200
|
||||
end
|
||||
|
||||
it 'embed redirects to builder for v3 when needed' do
|
||||
# These two tests are in the same testcase to test proper embed cache invalidation
|
||||
@user.stubs(:builder_enabled).returns(false)
|
||||
@user.stubs(:builder_enabled?).returns(false)
|
||||
visualization = CartoDB::Visualization::Member.new(id: @id).fetch
|
||||
visualization.version = 2
|
||||
visualization.store
|
||||
|
||||
login_as(@user, scope: @user.username)
|
||||
get public_visualizations_embed_map_path(id: @id), {}, @headers
|
||||
last_response.status.should eq 200
|
||||
|
||||
visualization.version = 3
|
||||
visualization.store
|
||||
|
||||
login_as(@user, scope: @user.username)
|
||||
get public_visualizations_embed_map_path(id: @id), {}, @headers
|
||||
last_response.status.should eq 302
|
||||
end
|
||||
end
|
||||
end
|
||||
end # GET /viz/:id
|
||||
|
||||
describe 'GET /tables/:id/public/table' do
|
||||
it 'returns 404 for private tables' do
|
||||
id = table_factory(privacy: ::UserTable::PRIVACY_PRIVATE).id
|
||||
|
||||
get "/tables/#{id}/public/table", {}, @headers
|
||||
last_response.status.should == 404
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /viz/:id/protected_public_map' do
|
||||
it 'returns 404 for private maps' do
|
||||
id = table_factory(privacy: ::UserTable::PRIVACY_PRIVATE).table_visualization.id
|
||||
|
||||
get "/viz/#{id}/protected_public_map", {}, @headers
|
||||
last_response.status.should == 404
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /viz/:id/protected_embed_map' do
|
||||
it 'returns 404 for private maps' do
|
||||
id = table_factory(privacy: ::UserTable::PRIVACY_PRIVATE).table_visualization.id
|
||||
|
||||
get "/viz/#{id}/protected_embed_map", {}, @headers
|
||||
last_response.status.should == 404
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /viz/:id/public_map' do
|
||||
it 'returns 403 for private maps' do
|
||||
id = table_factory(privacy: ::UserTable::PRIVACY_PRIVATE).table_visualization.id
|
||||
|
||||
get "/viz/#{id}/public_map", {}, @headers
|
||||
last_response.status.should == 403
|
||||
end
|
||||
|
||||
it 'go to password protected page if the viz is password protected' do
|
||||
id = factory.fetch('id')
|
||||
visualization = CartoDB::Visualization::Member.new(id: id).fetch
|
||||
visualization.version = 2
|
||||
visualization.password = 'foobar'
|
||||
visualization.privacy = Carto::Visualization::PRIVACY_PROTECTED
|
||||
visualization.store
|
||||
|
||||
get "/viz/#{id}/public_map", {}, @headers
|
||||
last_response.status.should == 200
|
||||
last_response.body.scan(/Insert your password/).present?.should == true
|
||||
end
|
||||
|
||||
it 'returns proper surrogate-keys' do
|
||||
id = table_factory(privacy: ::UserTable::PRIVACY_PUBLIC).table_visualization.id
|
||||
|
||||
get "/viz/#{id}/public_map", {}, @headers
|
||||
last_response.status.should == 200
|
||||
last_response.headers["Surrogate-Key"].should_not be_empty
|
||||
last_response.headers["Surrogate-Key"].should include(CartoDB::SURROGATE_NAMESPACE_PUBLIC_PAGES)
|
||||
end
|
||||
|
||||
it 'returns public map for org users' do
|
||||
org = OrganizationFactory.new.new_organization.save
|
||||
|
||||
user_a = create_user(quota_in_bytes: 123456789, table_quota: 400)
|
||||
user_org = CartoDB::UserOrganization.new(org.id, user_a.id)
|
||||
user_org.promote_user_to_admin
|
||||
|
||||
vis_id = new_table({user_id: user_a.id, privacy: ::UserTable::PRIVACY_PUBLIC}).save.reload.table_visualization.id
|
||||
|
||||
host! "#{org.name}.localhost.lan"
|
||||
get "/viz/#{vis_id}/public_map", @headers
|
||||
last_response.status.should == 200
|
||||
end
|
||||
|
||||
it 'go to password protected page if the organization viz is password protected' do
|
||||
org = OrganizationFactory.new.new_organization.save
|
||||
|
||||
user_a = create_user(quota_in_bytes: 123456789, table_quota: 400)
|
||||
user_org = CartoDB::UserOrganization.new(org.id, user_a.id)
|
||||
user_org.promote_user_to_admin
|
||||
id = factory(owner=user_a).fetch('id')
|
||||
visualization = CartoDB::Visualization::Member.new(id: id).fetch
|
||||
visualization.version = 2
|
||||
visualization.password = 'foobar'
|
||||
visualization.privacy = Carto::Visualization::PRIVACY_PROTECTED
|
||||
visualization.store
|
||||
|
||||
get "/viz/#{id}/public_map", {}, @headers
|
||||
|
||||
last_response.status.should == 302
|
||||
follow_redirect!
|
||||
last_response.status.should == 200
|
||||
last_response.body.scan(/Insert your password/).present?.should == true
|
||||
end
|
||||
|
||||
it 'does not load daily mapviews stats' do
|
||||
CartoDB::Visualization::Stats.expects(:mapviews).never
|
||||
CartoDB::Visualization::Stats.any_instance.expects(:to_poro).never
|
||||
CartoDB::Visualization.expects(:stats).never
|
||||
Carto::Visualization.expects(:stats).never
|
||||
|
||||
id = table_factory(privacy: ::UserTable::PRIVACY_PUBLIC).table_visualization.id
|
||||
|
||||
get public_visualizations_public_map_url(id: id), {}, @headers
|
||||
last_response.status.should == 200
|
||||
end
|
||||
|
||||
it 'serves X-Frame-Options: DENY' do
|
||||
id = table_factory(privacy: ::UserTable::PRIVACY_PUBLIC).table_visualization.id
|
||||
|
||||
get "/viz/#{id}/public_map", {}, @headers
|
||||
last_response.status.should == 200
|
||||
last_response.headers['X-Frame-Options'].should == 'DENY'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'public_visualizations_show_map' do
|
||||
|
||||
it 'does not load daily mapviews stats' do
|
||||
CartoDB::Visualization::Stats.expects(:mapviews).never
|
||||
CartoDB::Visualization::Stats.any_instance.expects(:to_poro).never
|
||||
CartoDB::Stats::APICalls.any_instance.expects(:get_api_calls_from_redis_source).never
|
||||
|
||||
CartoDB::Visualization.expects(:stats).never
|
||||
Carto::Visualization.expects(:stats).never
|
||||
|
||||
id = table_factory(privacy: ::UserTable::PRIVACY_PUBLIC).table_visualization.id
|
||||
|
||||
login_as(@user, scope: @user.username)
|
||||
get public_visualizations_show_map_url(id: id), {}, @headers
|
||||
last_response.status.should == 200
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'GET /viz/:id/public' do
|
||||
it 'returns public data for a table visualization' do
|
||||
id = table_factory(privacy: ::UserTable::PRIVACY_PUBLIC).table_visualization.id
|
||||
|
||||
get "/viz/#{id}/public", {}, @headers
|
||||
last_response.status.should == 200
|
||||
end
|
||||
|
||||
it 'returns a 404 if table is private' do
|
||||
id = table_factory.table_visualization.id
|
||||
|
||||
get "/viz/#{id}/public", {}, @headers
|
||||
last_response.status.should == 404
|
||||
end
|
||||
|
||||
it "redirects to embed_map if visualization is 'derived'" do
|
||||
map = FactoryGirl.create(:map, user_id: @user.id)
|
||||
derived_visualization = FactoryGirl.create(:derived_visualization, user_id: @user.id, map_id: map.id)
|
||||
id = derived_visualization.id
|
||||
|
||||
get "/viz/#{id}/public", {}, @headers
|
||||
last_response.status.should == 302
|
||||
follow_redirect!
|
||||
last_response.status.should == 200
|
||||
last_request.url.should =~ %r{.*#{id}/public_map.*}
|
||||
end
|
||||
end # GET /viz/:id/public
|
||||
|
||||
describe 'GET /tables/:id/embed_map' do
|
||||
it 'returns 404 for nonexisting tables when table name is used' do
|
||||
get "/tables/tablethatdoesntexist/embed_map", {}, @headers
|
||||
last_response.status.should == 404
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /viz/:name/embed_map' do
|
||||
it 'renders the view by passing a visualization name' do
|
||||
table = table_factory(privacy: ::UserTable::PRIVACY_PUBLIC)
|
||||
name = table.table_visualization.name
|
||||
|
||||
get "/viz/#{URI::encode(name)}/embed_map", {}, @headers
|
||||
last_response.status.should == 200
|
||||
last_response.headers["X-Cache-Channel"].should_not be_empty
|
||||
last_response.headers["X-Cache-Channel"].should include(table.name)
|
||||
last_response.headers["X-Cache-Channel"].should include(table.table_visualization.varnish_key)
|
||||
last_response.headers["Surrogate-Key"].should_not be_empty
|
||||
last_response.headers["Surrogate-Key"].should include(CartoDB::SURROGATE_NAMESPACE_PUBLIC_PAGES)
|
||||
last_response.headers["Surrogate-Key"].should include(table.table_visualization.surrogate_key)
|
||||
end
|
||||
|
||||
it 'renders embed map error page if visualization private' do
|
||||
table = table_factory
|
||||
put "/api/v1/tables/#{table.id}?api_key=#{@api_key}",
|
||||
{ privacy: 0 }.to_json, @headers
|
||||
|
||||
name = table.table_visualization.name
|
||||
name = URI::encode(name)
|
||||
|
||||
login_as(@user, scope: @user.username)
|
||||
|
||||
get "/viz/#{name}/embed_map", {}, @headers
|
||||
last_response.status.should == 403
|
||||
last_response.body.should include("Map or dataset not found, or with restricted access.")
|
||||
end
|
||||
|
||||
it 'renders embed map error when an exception is raised' do
|
||||
login_as(@user, scope: @user.username)
|
||||
|
||||
get "/viz/220d2f46-b371-11e4-93f7-080027880ca6/embed_map", {}, @headers
|
||||
last_response.status.should == 404
|
||||
end
|
||||
|
||||
it 'doesnt serve X-Frame-Options: DENY on embedded with name' do
|
||||
table = table_factory(privacy: ::UserTable::PRIVACY_PUBLIC)
|
||||
name = table.table_visualization.name
|
||||
|
||||
get "/viz/#{URI::encode(name)}/embed_map", {}, @headers
|
||||
last_response.status.should == 200
|
||||
last_response.headers.include?('X-Frame-Options').should_not == true
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /viz/:id/embed_map' do
|
||||
it 'caches and serves public embed map successful responses' do
|
||||
id = table_factory(privacy: ::UserTable::PRIVACY_PUBLIC).table_visualization.id
|
||||
embed_redis_cache = EmbedRedisCache.new
|
||||
|
||||
embed_redis_cache.get(id, https=false).should == nil
|
||||
get "/viz/#{id}/embed_map", {}, @headers
|
||||
last_response.status.should == 200
|
||||
|
||||
# The https key/value pair should be differenent
|
||||
embed_redis_cache.get(id, https=true).should == nil
|
||||
last_response.status.should == 200
|
||||
|
||||
# It should be cached after the first request
|
||||
embed_redis_cache.get(id, https=false).should_not be_nil
|
||||
first_response = last_response
|
||||
|
||||
get "/viz/#{id}/embed_map", {}, @headers
|
||||
last_response.status.should == 200
|
||||
# Headers of both responses should be the same excluding some
|
||||
remove_changing = lambda {|h| h.reject {|k, v| ['X-Request-Id', 'X-Runtime'].include?(k)} }
|
||||
remove_changing.call(first_response.headers).should == remove_changing.call(last_response.headers)
|
||||
first_response.body.should == last_response.body
|
||||
end
|
||||
|
||||
it 'doesnt serve X-Frame-Options: DENY on embedded' do
|
||||
id = table_factory(privacy: ::UserTable::PRIVACY_PUBLIC).table_visualization.id
|
||||
|
||||
get "/viz/#{id}/embed_map", {}, @headers
|
||||
last_response.status.should == 200
|
||||
last_response.headers.include?('X-Frame-Options').should_not == true
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /viz/:name/track_embed' do
|
||||
it 'renders the view by passing a visualization name' do
|
||||
login_as(@user, scope: @user.username)
|
||||
|
||||
get "/viz/track_embed", {}, @headers
|
||||
last_response.status.should == 200
|
||||
end
|
||||
|
||||
it 'doesnt serve X-Frame-Options: DENY for track_embed' do
|
||||
login_as(@user, scope: @user.username)
|
||||
|
||||
get "/viz/track_embed", {}, @headers
|
||||
last_response.status.should == 200
|
||||
last_response.headers.include?('X-Frame-Options').should_not == true
|
||||
end
|
||||
end
|
||||
|
||||
describe 'non existent visualization' do
|
||||
it 'returns 404' do
|
||||
login_as(@user, scope: @user.username)
|
||||
|
||||
get "/viz/220d2f46-b371-11e4-93f7-080027880ca6?api_key=#{@api_key}", {}, @headers
|
||||
last_response.status.should == 404
|
||||
|
||||
get "/viz/220d2f46-b371-11e4-93f7-080027880ca6/public?api_key=#{@api_key}", {}, @headers
|
||||
last_response.status.should == 404
|
||||
|
||||
get "/viz/220d2f46-b371-11e4-93f7-080027880ca6/embed_map?api_key=#{@api_key}", {}, @headers
|
||||
last_response.status.should == 404
|
||||
end
|
||||
end # non existent visualization
|
||||
|
||||
describe 'org user visualization redirection' do
|
||||
it 'if A shares a (shared) vis link to B with A username, performs a redirect to B username' do
|
||||
Carto::ApiKey.any_instance.stubs(:save_cdb_conf_info)
|
||||
CartoDB::UserModule::DBService.any_instance.stubs(:move_to_own_schema).returns(nil)
|
||||
CartoDB::TablePrivacyManager.any_instance.stubs(
|
||||
:set_from_table_privacy => nil,
|
||||
:propagate_to_varnish => nil
|
||||
)
|
||||
|
||||
::User.any_instance.stubs(
|
||||
after_create: nil
|
||||
)
|
||||
|
||||
CartoDB::UserModule::DBService.any_instance.stubs(
|
||||
grant_user_in_database: nil,
|
||||
grant_publicuser_in_database: nil,
|
||||
set_user_privileges_at_db: nil,
|
||||
set_statement_timeouts: nil,
|
||||
set_user_as_organization_member: nil,
|
||||
rebuild_quota_trigger: nil,
|
||||
setup_organization_user_schema: nil,
|
||||
set_database_search_path: nil,
|
||||
cartodb_extension_version_pre_mu?: false,
|
||||
load_cartodb_functions: nil,
|
||||
create_schema: nil,
|
||||
move_tables_to_schema: nil,
|
||||
create_public_db_user: nil,
|
||||
monitor_user_notification: nil,
|
||||
enable_remote_db_user: nil
|
||||
)
|
||||
|
||||
Carto::NamedMaps::Api.any_instance.stubs(get: nil, create: true, update: true)
|
||||
|
||||
Table.any_instance.stubs(perform_cartodb_function: nil,
|
||||
update_cdb_tablemetadata: nil,
|
||||
update_table_pg_stats: nil,
|
||||
create_table_in_database!: nil,
|
||||
get_table_id: 1,
|
||||
grant_select_to_tiler_user: nil,
|
||||
cartodbfy: nil,
|
||||
set_the_geom_column!: nil)
|
||||
|
||||
# --------TEST ITSELF-----------
|
||||
|
||||
org = Organization.new
|
||||
org.name = 'vis-spec-org-2'
|
||||
org.quota_in_bytes = 1024 ** 3
|
||||
org.seats = 10
|
||||
org.builder_enabled = false
|
||||
org.save
|
||||
|
||||
::User.any_instance.stubs(:remaining_quota).returns(1000)
|
||||
user_a = create_user(username: 'user-a', quota_in_bytes: 123456789, table_quota: 400)
|
||||
user_org = CartoDB::UserOrganization.new(org.id, user_a.id)
|
||||
user_org.promote_user_to_admin
|
||||
org.reload
|
||||
user_a.reload
|
||||
|
||||
user_b = create_user(username: 'user-b',
|
||||
quota_in_bytes: 123456789,
|
||||
table_quota: 400,
|
||||
organization: org,
|
||||
account_type: 'ORGANIZATION USER')
|
||||
|
||||
# Needed because after_create is stubbed
|
||||
user_a.create_api_keys
|
||||
user_b.create_api_keys
|
||||
|
||||
vis_id = factory(user_a).fetch('id')
|
||||
vis = CartoDB::Visualization::Member.new(id: vis_id).fetch
|
||||
vis.privacy = CartoDB::Visualization::Member::PRIVACY_PRIVATE
|
||||
vis.store
|
||||
|
||||
login_host(user_b, org)
|
||||
|
||||
get CartoDB.url(@mock_context, 'public_table', params: { id: vis.name }, user: user_a)
|
||||
last_response.status.should be(404)
|
||||
|
||||
['public_visualizations_public_map', 'public_tables_embed_map'].each do |forbidden_endpoint|
|
||||
get CartoDB.url(@mock_context, forbidden_endpoint, params: { id: vis.name }, user: user_a)
|
||||
follow_redirects
|
||||
last_response.status.should be(403), "#{forbidden_endpoint} is #{last_response.status}"
|
||||
end
|
||||
|
||||
perm = vis.permission
|
||||
perm.set_user_permission(user_b, CartoDB::Permission::ACCESS_READONLY)
|
||||
perm.save
|
||||
|
||||
get CartoDB.url(@mock_context, 'public_table', params: { id: vis.name }, user: user_a)
|
||||
last_response.status.should == 302
|
||||
# First we'll get redirected to the public map url
|
||||
follow_redirect!
|
||||
# Now url will get rewritten to current user
|
||||
last_response.status.should == 302
|
||||
url = CartoDB.base_url(org.name, user_b.username) +
|
||||
CartoDB.path(self, 'public_visualizations_show', id: "#{user_a.username}.#{vis.name}") + "?redirected=true"
|
||||
last_response.location.should eq url
|
||||
|
||||
['public_visualizations_public_map', 'public_tables_embed_map'].each do |forbidden_endpoint|
|
||||
get CartoDB.url(@mock_context, forbidden_endpoint, params: { id: vis.name }, user: user_a)
|
||||
follow_redirects
|
||||
last_response.status.should be(200), "#{forbidden_endpoint} is #{last_response.status}"
|
||||
last_response.length.should >= 100
|
||||
end
|
||||
org.destroy
|
||||
end
|
||||
|
||||
# @see https://github.com/CartoDB/cartodb/issues/6081
|
||||
it 'If logged user navigates to legacy url from org user without org name, gets redirected properly' do
|
||||
Carto::ApiKey.any_instance.stubs(:save_cdb_conf_info)
|
||||
CartoDB::UserModule::DBService.any_instance.stubs(:move_to_own_schema).returns(nil)
|
||||
CartoDB::TablePrivacyManager.any_instance.stubs(
|
||||
set_from_table_privacy: nil,
|
||||
propagate_to_varnish: nil
|
||||
)
|
||||
|
||||
::User.any_instance.stubs(
|
||||
after_create: nil
|
||||
)
|
||||
|
||||
CartoDB::UserModule::DBService.any_instance.stubs(
|
||||
grant_user_in_database: nil,
|
||||
grant_publicuser_in_database: nil,
|
||||
set_user_privileges_at_db: nil,
|
||||
set_statement_timeouts: nil,
|
||||
set_user_as_organization_member: nil,
|
||||
rebuild_quota_trigger: nil,
|
||||
setup_organization_user_schema: nil,
|
||||
set_database_search_path: nil,
|
||||
cartodb_extension_version_pre_mu?: false,
|
||||
load_cartodb_functions: nil,
|
||||
create_schema: nil,
|
||||
move_tables_to_schema: nil,
|
||||
create_public_db_user: nil,
|
||||
monitor_user_notification: nil,
|
||||
enable_remote_db_user: nil
|
||||
)
|
||||
|
||||
Carto::NamedMaps::Api.any_instance.stubs(get: nil, create: true, update: true)
|
||||
Table.any_instance.stubs(
|
||||
perform_cartodb_function: nil,
|
||||
update_cdb_tablemetadata: nil,
|
||||
update_table_pg_stats: nil,
|
||||
create_table_in_database!: nil,
|
||||
get_table_id: 1,
|
||||
grant_select_to_tiler_user: nil,
|
||||
cartodbfy: nil,
|
||||
set_the_geom_column!: nil
|
||||
)
|
||||
|
||||
# --------TEST ITSELF-----------
|
||||
|
||||
org = Organization.new
|
||||
org.name = 'vis-spec-org'
|
||||
org.quota_in_bytes = 1024**3
|
||||
org.seats = 10
|
||||
org.builder_enabled = false
|
||||
org.save
|
||||
|
||||
::User.any_instance.stubs(:remaining_quota).returns(1000)
|
||||
user_a = create_user(quota_in_bytes: 123456789, table_quota: 400)
|
||||
user_org = CartoDB::UserOrganization.new(org.id, user_a.id)
|
||||
user_org.promote_user_to_admin
|
||||
org.reload
|
||||
user_a.reload
|
||||
|
||||
user_b = create_user(quota_in_bytes: 123456789, table_quota: 400)
|
||||
|
||||
# Needed because after_create is stubbed
|
||||
user_a.create_api_keys
|
||||
user_b.create_api_keys
|
||||
|
||||
vis_id = factory(user_a).fetch('id')
|
||||
vis = CartoDB::Visualization::Member.new(id: vis_id).fetch
|
||||
vis.privacy = CartoDB::Visualization::Member::PRIVACY_PUBLIC
|
||||
vis.store
|
||||
|
||||
login_host(user_b)
|
||||
|
||||
# dirty but effective trick, generate the url as if were for a non-org user, then replace usernames
|
||||
# to respect format and just have no organization
|
||||
destination_url = CartoDB.url(@mock_context, 'public_visualizations_public_map',
|
||||
params: { id: vis.name }, user: user_b)
|
||||
.sub(user_b.username, user_a.username)
|
||||
|
||||
get destination_url
|
||||
last_response.status.should be(302)
|
||||
last_response.headers["Location"].should eq CartoDB.url(@mock_context, 'public_visualizations_public_map',
|
||||
params: { id: vis.id, redirected: true }, user: user_a)
|
||||
follow_redirect!
|
||||
last_response.status.should be(200)
|
||||
|
||||
org.destroy
|
||||
end
|
||||
end
|
||||
|
||||
describe 'find visualizations by name' do
|
||||
before(:all) do
|
||||
@organization = create_organization_with_users(name: unique_name('organization'))
|
||||
@org_user = @organization.users.first
|
||||
bypass_named_maps
|
||||
@table = new_table(user_id: @org_user.id, privacy: ::UserTable::PRIVACY_PUBLIC).save.reload
|
||||
@faketable = new_table(user_id: @user.id, privacy: ::UserTable::PRIVACY_PUBLIC).save.reload
|
||||
@faketable_name = @faketable.table_visualization.name
|
||||
end
|
||||
|
||||
it 'finds visualization by org and name' do
|
||||
url = CartoDB.url(@mock_context, 'public_table', params: { id: @table.table_visualization.name }, user: @org_user)
|
||||
url = url.sub("/u/#{@org_user.username}", '')
|
||||
|
||||
get url
|
||||
last_response.status.should == 200
|
||||
end
|
||||
|
||||
it 'does not find visualizations outside org' do
|
||||
url = CartoDB.url(@mock_context, 'public_table', params: { id: @faketable_name }, user: @org_user)
|
||||
url = url.sub("/u/#{@org_user.username}", '')
|
||||
|
||||
get url
|
||||
last_response.status.should == 404
|
||||
end
|
||||
|
||||
it 'finds visualization by user and public.name' do
|
||||
url = CartoDB.url(@mock_context, 'public_table',
|
||||
params: { id: "public.#{@table.table_visualization.name}" }, user: @org_user)
|
||||
|
||||
get url
|
||||
last_response.status.should == 200
|
||||
end
|
||||
|
||||
it 'finds visualization by user and public.id' do
|
||||
url = CartoDB.url(@mock_context, 'public_table',
|
||||
params: { id: "public.#{@table.table_visualization.id}" }, user: @org_user)
|
||||
|
||||
get url
|
||||
last_response.status.should == 200
|
||||
end
|
||||
|
||||
it 'does not find visualizations outside user with public schema' do
|
||||
url = CartoDB.url(@mock_context, 'public_table',
|
||||
params: { id: "public.#{@faketable_name}" }, user: @org_user)
|
||||
url = url.sub("/u/#{@org_user.username}", '')
|
||||
|
||||
get url
|
||||
last_response.status.should == 404
|
||||
end
|
||||
|
||||
it 'does not try to search visualizations with invalid user/org' do
|
||||
url = CartoDB.url(@mock_context, 'public_table', params: { id: "public.#{@table.name}" }, user: @org_user)
|
||||
url = url.sub("/u/#{@org_user.username}", '/u/invalidus3r')
|
||||
|
||||
get url
|
||||
last_response.status.should == 404
|
||||
end
|
||||
end
|
||||
|
||||
def login_host(user, org = nil)
|
||||
login_as(user, scope: user.username)
|
||||
host! "#{org.nil? ? user.username : org.name}.localhost.lan"
|
||||
end
|
||||
|
||||
def follow_redirects(limit = 10)
|
||||
while last_response.status == 302 && (limit -= 1) > 0 do
|
||||
follow_redirect!
|
||||
end
|
||||
end
|
||||
|
||||
def factory(owner=nil)
|
||||
owner = @user if owner.nil?
|
||||
map = Map.create(user_id: owner.id)
|
||||
payload = {
|
||||
name: unique_name('viz'),
|
||||
tags: ['foo', 'bar'],
|
||||
map_id: map.id,
|
||||
description: 'bogus',
|
||||
type: 'derived'
|
||||
}
|
||||
|
||||
with_host "#{owner.username}.localhost.lan" do
|
||||
post "/api/v1/viz?api_key=#{owner.api_key}", payload.to_json
|
||||
end
|
||||
|
||||
|
||||
JSON.parse(last_response.body)
|
||||
end
|
||||
|
||||
def table_factory(attrs = {})
|
||||
new_table(attrs.merge(user_id: @user.id)).save.reload
|
||||
end
|
||||
|
||||
end # Admin::VisualizationsController
|
||||
Reference in New Issue
Block a user