cartodb-4.42/spec/requests/carto/api/users_controller_spec.rb
2024-04-06 05:25:13 +00:00

525 lines
19 KiB
Ruby

require_relative '../../../spec_helper_min'
require 'support/helpers'
require_relative '../../../../app/controllers/carto/api/users_controller'
describe Carto::Api::UsersController do
include_context 'organization with users helper'
include Rack::Test::Methods
include Warden::Test::Helpers
include HelperMethods
before(:all) do
@headers = { 'CONTENT_TYPE' => 'application/json' }
FactoryGirl.create(:notification, organization: @carto_organization)
end
before(:each) do
::User.any_instance.stubs(:create_in_central).returns(true)
::User.any_instance.stubs(:update_in_central).returns(true)
FactoryGirl.create(:carto_visualization, user: @carto_org_user_owner, privacy: Carto::Visualization::PRIVACY_PUBLIC)
FactoryGirl.create(:carto_visualization, user: @carto_org_user_owner,
privacy: Carto::Visualization::PRIVACY_PRIVATE)
FactoryGirl.create(:carto_visualization, user: @carto_org_user_owner, privacy: Carto::Visualization::PRIVACY_LINK)
FactoryGirl.create(:carto_visualization, user: @carto_org_user_owner, privacy: Carto::Visualization::PRIVACY_LINK)
FactoryGirl.create(:carto_visualization, user: @carto_org_user_owner,
privacy: Carto::Visualization::PRIVACY_PROTECTED, password: 'a')
FactoryGirl.create(:carto_visualization, user: @carto_org_user_owner,
privacy: Carto::Visualization::PRIVACY_PROTECTED, password: 'a')
FactoryGirl.create(:carto_visualization, user: @carto_org_user_owner,
privacy: Carto::Visualization::PRIVACY_PROTECTED, password: 'a')
end
describe 'me' do
it 'contains hubspot_form_ids in config' do
CartoDB::Hubspot.any_instance.stubs(:enabled?).returns(true)
CartoDB::Hubspot.any_instance.stubs(:token).returns('something')
params = { user_domain: @org_user_1.username, api_key: @org_user_1.api_key }
get_json api_v3_users_me_url(params), @headers do |response|
expect(response.status).to eq(200)
expect(response.body).to have_key(:config)
expect(response.body[:config]).to have_key(:hubspot_form_ids)
end
CartoDB::Hubspot.any_instance.unstub(:enabled?)
CartoDB::Hubspot.any_instance.unstub(:token)
end
it 'returns the user info even when locked' do
@org_user_1.update(state: 'locked')
params = { user_domain: @org_user_1.username, api_key: @org_user_1.api_key }
get_json api_v3_users_me_url(params), @headers do |response|
expect(response.status).to eq(200)
expect(response.body[:user_data][:username]).to eq(@org_user_1.username)
end
@org_user_1.update(state: 'active')
end
it 'returns a hash with current user info' do
params = { user_domain: @carto_org_user_owner.username, api_key: @carto_org_user_owner.api_key }
get_json api_v3_users_me_url(params), @headers do |response|
expect(response.status).to eq(200)
expect(response.body[:default_fallback_basemap].with_indifferent_access).to eq(@carto_org_user_owner.default_basemap)
dashboard_notifications = @carto_org_user_owner.notifications_for_category(:dashboard)
expect(response.body[:dashboard_notifications]).to eq(dashboard_notifications)
expect(response.body[:organization_notifications].count).to eq(1)
expect(response.body[:organization_notifications].first[:icon]).to eq(
@carto_org_user_owner.received_notifications.unread.first.icon
)
expect(response.body[:can_change_email]).to eq(@carto_org_user_owner.can_change_email?)
expect(response.body[:auth_username_password_enabled]).to eq(true)
expect(response.body[:can_change_password]).to eq(true)
expect(response.body[:plan_name]).to eq('ORGANIZATION USER')
expect(response.body[:services]).to eq(@carto_org_user_owner.get_oauth_services.map(&:symbolize_keys))
expect(response.body[:google_sign_in]).to eq(@carto_org_user_owner.google_sign_in)
expect(response.body[:user_data][:public_privacy_map_count]).to eq 1
expect(response.body[:user_data][:link_privacy_map_count]).to eq 2
expect(response.body[:user_data][:password_privacy_map_count]).to eq 3
expect(response.body[:user_data][:private_privacy_map_count]).to eq 1
end
end
it 'returns a hash with only config if there is no authenticated user' do
get_json api_v3_users_me_url, @headers do |response|
expect(response.status).to eq(200)
expect(response.body).to have_key(:config)
expect(response.body[:user_frontend_version]).to eq(CartoDB::Application.frontend_version)
end
end
context 'license_expiration field' do
before(:each) do
@expiration_date = Time.parse("2020-11-05T00:00:00.000+00:00")
end
after(:each) do
Carto::Api::UsersController.any_instance.unstub(:license_expiration_date)
end
it 'is nil for cloud' do
Carto::Api::UsersController.any_instance.stubs(:license_expiration_date).returns(@expiration_date)
Cartodb.with_config(cartodb_com_hosted: false) do
get_json api_v3_users_me_url, @headers do |response|
expect(response.status).to eq(200)
expect(response.body[:license_expiration]).to be_nil
end
end
end
it 'is nil if the license_expiration_date does not exist (gear not loaded)' do
Cartodb.with_config(cartodb_com_hosted: true) do
get_json api_v3_users_me_url, @headers do |response|
expect(response.status).to eq(200)
expect(response.body[:license_expiration]).to be_nil
end
end
end
it 'gets the date from the license' do
Carto::Api::UsersController.any_instance.stubs(:license_expiration_date).returns(@expiration_date)
params = { user_domain: @org_user_1.username, api_key: @org_user_1.api_key }
Cartodb.with_config(cartodb_com_hosted: true) do
get_json api_v3_users_me_url(params), @headers do |response|
expect(response.status).to eq(200)
expect(response.body[:license_expiration]).to eq "2020-11-05T00:00:00.000+00:00"
end
end
end
end
context 'is_enterprise field' do
before(:all) do
@params = { user_domain: @org_user_1.username, api_key: @org_user_1.api_key }
end
after(:each) do
User.any_instance.unstub(:account_type)
end
it 'returns false for Individual plan' do
Carto::User.any_instance.stubs(account_type: 'Individual')
get_json api_v3_users_me_url(@params), @headers do |response|
expect(response.status).to eq(200)
expect(response.body[:user_data][:is_enterprise]).to eq(false)
end
end
it 'returns true for an enterprise plan' do
Carto::User.any_instance.stubs(account_type: 'ENTERPRISE LUMP-SUM')
get_json api_v3_users_me_url(@params), @headers do |response|
expect(response.status).to eq(200)
expect(response.body[:user_data][:is_enterprise]).to eq(true)
end
end
end
end
describe 'update_me' do
context 'account updates' do
before(:each) do
@user = FactoryGirl.create(:user, password: 'foobarbaz', password_confirmation: 'foobarbaz')
end
after(:each) do
@user.destroy
end
let(:url_options) do
{
user_domain: @user.username,
user_id: @user.id,
api_key: @user.api_key
}
end
it 'gives an error if password is the same as password_confirmation' do
last_change = @user.last_password_change_date
payload = {
user: {
email: 'foo@bar.baz',
password_confirmation: 'foobarbaz',
new_password: 'foobarbaz',
confirm_password: 'foobarbaz'
}
}
put_json api_v3_users_update_me_url(url_options), payload, @headers do |response|
expect(response.status).to eq(400)
expect(response.body[:errors]).to have_key(:new_password)
expect(response.body[:errors][:new_password]).to eq ['New password cannot be the same as old password']
@user.reload
expect(@user.last_password_change_date).to eq(last_change)
end
end
it 'gives a status code 200 if payload is empty' do
put_json api_v3_users_update_me_url(url_options), {}, @headers do |response|
expect(response.status).to eq(200)
end
end
it 'gives an error if there is no old password' do
payload = { user: { email: 'foo1@bar.baz' } }
put_json api_v3_users_update_me_url(url_options), payload, @headers do |response|
expect(response.status).to eq(403)
expect(response.body[:message]).to eq("Error updating your account details")
expect(response.body[:errors]).to have_key(:password)
end
end
it 'updates account if old password is correct' do
payload = { user: { email: 'foo1@bar.baz',
password_confirmation: 'foobarbaz' } }
put_json api_v3_users_update_me_url(url_options), payload, @headers do |response|
expect(response.status).to eq(200)
@user.refresh
expect(@user.email).to eq('foo1@bar.baz')
end
end
it 'gives an error if email is invalid' do
payload = { user: { email: 'foo@',
password_confirmation: 'foobarbaz' } }
put_json api_v3_users_update_me_url(url_options), payload, @headers do |response|
expect(response.status).to eq(400)
expect(response.body[:message]).to eq("Error updating your account details")
expect(response.body[:errors]).to have_key(:email)
end
end
it 'gives an error if old password is invalid' do
payload = { user: { password_confirmation: 'idontknow', new_password: 'barbaz', confirm_password: 'barbaz' } }
put_json api_v3_users_update_me_url(url_options), payload, @headers do |response|
expect(response.status).to eq(403)
expect(response.body[:message]).to eq("Error updating your account details")
expect(response.body[:errors]).to have_key(:password)
end
end
it 'gives an error if new password and confirmation are not the same' do
payload = { user: { password_confirmation: 'foobarbaz', new_password: 'foofoo', confirm_password: 'barbar' } }
put_json api_v3_users_update_me_url(url_options), payload, @headers do |response|
expect(response.status).to eq(400)
expect(response.body[:message]).to eq("Error updating your account details")
expect(response.body[:errors]).to have_key(:new_password)
end
end
it 'returns 401 if user is not logged in' do
put_json api_v3_users_update_me_url(url_options.except(:api_key)), @headers do |response|
expect(response.status).to eq(401)
end
end
it 'updates account data for the given user' do
last_change = @user.last_password_change_date
payload = {
user: {
email: 'foo@bar.baz',
password_confirmation: 'foobarbaz',
new_password: 'bazbarfoo',
confirm_password: 'bazbarfoo'
}
}
put_json api_v3_users_update_me_url(url_options), payload, @headers do |response|
expect(response.status).to eq(200)
@user.refresh
expect(@user.email).to eq('foo@bar.baz')
expect(@user.last_password_change_date).to_not eq(last_change)
end
end
context 'multifactor authentication' do
it 'creates a multifactor authentication' do
payload = { user: { password_confirmation: 'foobarbaz', mfa: true } }
put_json api_v3_users_update_me_url(url_options), payload, @headers
@user.reload.user_multifactor_auths.should_not be_empty
end
it 'removes the multifactor authentications' do
FactoryGirl.create(:totp, user_id: @user.id)
payload = { user: { password_confirmation: 'foobarbaz', mfa: false } }
@user.reload.user_multifactor_auths.should_not be_empty
put_json api_v3_users_update_me_url(url_options), payload, @headers
last_response.status.should eq 200
@user.reload.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!'))
payload = { user: { password_confirmation: 'foobarbaz', mfa: false } }
put_json api_v3_users_update_me_url(url_options), payload, @headers
last_response.status.should eq 400
@user.reload.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))
payload = { user: { password_confirmation: 'foobarbaz', mfa: true } }
@user.expects(:save).never
put_json api_v3_users_update_me_url(url_options), payload, @headers
last_response.status.should eq 400
end
end
end
context 'profile updates' do
before(:each) do
@user = FactoryGirl.create(:user, password: 'foobarbaz', password_confirmation: 'foobarbaz')
end
after(:each) do
@user.destroy
end
let(:url_options) do
{
user_domain: @user.username,
user_id: @user.id,
api_key: @user.api_key
}
end
it 'updates profile data for the given user' do
payload = {
user: {
name: 'Foo',
last_name: 'Bar',
website: 'https://carto.rocks',
description: 'Foo Bar Baz',
location: 'Anywhere',
twitter_username: 'carto',
disqus_shortname: 'carto',
avatar_url: 'http://carto.rocks/avatar.jpg',
password_confirmation: 'foobarbaz'
}
}
put_json api_v3_users_update_me_url(url_options), payload, @headers do |response|
expect(response.status).to eq(200)
expect(response.body[:name]).to eq('Foo')
expect(response.body[:last_name]).to eq('Bar')
expect(response.body[:website]).to eq('https://carto.rocks')
expect(response.body[:description]).to eq('Foo Bar Baz')
expect(response.body[:location]).to eq('Anywhere')
expect(response.body[:twitter_username]).to eq('carto')
expect(response.body[:disqus_shortname]).to eq('carto')
expect(response.body[:avatar_url]).to eq('http://carto.rocks/avatar.jpg')
end
end
it 'does not update profile data if old password is wrong' do
payload = {
user: {
name: 'Foo2',
password_confirmation: 'prapra'
}
}
put_json api_v3_users_update_me_url(url_options), payload, @headers do |response|
expect(response.status).to eq(403)
@user.reload
@user.username.should_not eq 'Foo2'
end
end
it 'does not update profile data if password_confirmation is missing' do
payload = {
user: {
name: 'Foo2'
}
}
put_json api_v3_users_update_me_url(url_options), payload, @headers do |response|
expect(response.status).to eq(403)
@user.reload
@user.username.should_not eq 'Foo2'
end
end
it 'does not update fields not present in the user hash' do
payload = {
user: {
name: 'Foo',
last_name: 'Bar',
website: 'https://carto.rocks',
password_confirmation: 'foobarbaz'
}
}
old_description = @user.description
old_location = @user.location
old_twitter_username = @user.twitter_username
put_json api_v3_users_update_me_url(url_options), payload, @headers do |response|
expect(response.status).to eq(200)
expect(response.body[:name]).to eq('Foo')
expect(response.body[:last_name]).to eq('Bar')
expect(response.body[:website]).to eq('https://carto.rocks')
expect(response.body[:description]).to eq(old_description)
expect(response.body[:location]).to eq(old_location)
expect(response.body[:twitter_username]).to eq(old_twitter_username)
end
end
it 'sets field to nil if key is present in the hash with a nil value' do
fields_to_check = [
:name, :last_name, :website, :description, :location, :twitter_username,
:disqus_shortname, :available_for_hire
]
fields_to_check.each do |field|
payload = { user: { field => nil,
password_confirmation: 'foobarbaz' } }
put_json api_v3_users_update_me_url(url_options), payload, @headers do |response|
expect(response.status).to eq(200)
@user.refresh
expect(@user.values[field]).to be_nil
end
end
end
it 'returns 401 if user is not logged in' do
payload = { user: { name: 'Foo',
password_confirmation: 'foobarbaz' } }
put_json api_v3_users_update_me_url(url_options.except(:api_key)), payload, @headers do |response|
expect(response.status).to eq(401)
end
end
end
end
describe 'delete_me' do
before(:each) do
@user = FactoryGirl.create(:user, password: 'foobarbaz', password_confirmation: 'foobarbaz')
User.any_instance.stubs(:delete_in_central)
end
let(:url_options) do
{
user_domain: @user.username,
api_key: @user.api_key
}
end
it 'deletes the authenticated user' do
payload = { deletion_password_confirmation: 'foobarbaz' }
delete_json api_v3_users_delete_me_url(url_options), payload, @headers do |response|
expect(response.status).to eq(200)
expect(Carto::User.exists?(@user.id)).to be_false
end
end
it 'deletes the authenticated user even when locked' do
@user.update(state: 'locked')
payload = { deletion_password_confirmation: 'foobarbaz' }
delete_json api_v3_users_delete_me_url(url_options), payload, @headers do |response|
expect(response.status).to eq(200)
expect(Carto::User.exists?(@user.id)).to be_false
end
end
context 'failures in deletion' do
after(:each) do
@user.destroy
end
it 'gives an error if deletion password confirmation is invalid' do
payload = { deletion_password_confirmation: 'idontknow' }
delete_json api_v3_users_delete_me_url(url_options), payload, @headers do |response|
expect(response.status).to eq(400)
expect(response.body[:message]).to eq("Error deleting user: Password does not match")
end
end
it 'returns 401 if user is not logged in' do
delete_json api_v3_users_delete_me_url(url_options.except(:api_key)), @headers do |response|
expect(response.status).to eq(401)
end
end
end
end
end