require_relative '../spec_helper' require 'helpers/user_part_helper' describe User do include UserPartHelper include_context 'user spec configuration' describe 'central synchronization' do it 'should create remote user in central if needed' do pending "Central API credentials not provided" unless ::User.new.sync_data_with_cartodb_central? organization = create_org('testorg', 500.megabytes, 1) user = create_user email: 'user1@testorg.com', username: 'user1', password: 'user11', account_type: 'ORGANIZATION USER' user.organization = organization user.save Cartodb::Central.any_instance.expects(:create_organization_user).with(organization.name, user.allowed_attributes_to_central(:create)).once user.create_in_central.should be_true organization.destroy end end it "should have many tables" do @user2.tables.should be_empty create_table :user_id => @user2.id, :name => 'My first table', :privacy => UserTable::PRIVACY_PUBLIC @user2.reload @user2.tables.all.should == [UserTable.first(:user_id => @user2.id)] end describe '#shared_tables' do it 'Checks that shared tables include not only owned ones' do require_relative '../../app/models/visualization/collection' CartoDB::Varnish.any_instance.stubs(:send_command).returns(true) bypass_named_maps # No need to really touch the DB for the permissions Table::any_instance.stubs(:add_read_permission).returns(nil) # We're leaking tables from some tests, make sure there are no tables @user.tables.all.each { |t| t.destroy } @user2.tables.all.each { |t| t.destroy } table = Table.new table.user_id = @user.id table.save.reload table2 = Table.new table2.user_id = @user.id table2.save.reload table3 = Table.new table3.user_id = @user2.id table3.name = 'sharedtable' table3.save.reload table4 = Table.new table4.user_id = @user2.id table4.name = 'table4' table4.save.reload # Only owned tables user_tables = tables_including_shared(@user) user_tables.count.should eq 2 # Grant permission user2_vis = CartoDB::Visualization::Collection.new.fetch(user_id: @user2.id, name: table3.name).first permission = user2_vis.permission permission.acl = [ { type: CartoDB::Permission::TYPE_USER, entity: { id: @user.id, username: @user.username }, access: CartoDB::Permission::ACCESS_READONLY } ] permission.save # Now owned + shared... user_tables = tables_including_shared(@user) user_tables.count.should eq 3 contains_shared_table = false user_tables.each{ |item| contains_shared_table ||= item.id == table3.id } contains_shared_table.should eq true contains_shared_table = false user_tables.each{ |item| contains_shared_table ||= item.id == table4.id } contains_shared_table.should eq false @user.tables.all.each { |t| t.destroy } @user2.tables.all.each { |t| t.destroy } end end it "should create a client_application for each user" do @user.client_application.should_not be_nil end it "should reset its client application" do old_key = @user.client_application.key @user.reset_client_application! @user.reload @user.client_application.key.should_not == old_key end it "should not regenerate the api_key after saving" do expect { @user.save }.to_not change { @user.api_key } end it "should remove its user tables, layers and data imports after deletion" do doomed_user = create_user(email: 'doomed2@example.com', username: 'doomed2', password: 'doomed123') data_import = DataImport.create(user_id: doomed_user.id, data_source: fake_data_path('clubbing.csv')).run_import! doomed_user.add_layer Layer.create(kind: 'carto') table_id = data_import.table_id uuid = UserTable.where(id: table_id).first.table_visualization.id CartoDB::Varnish.any_instance.expects(:purge) .with("#{doomed_user.database_name}.*") .at_least(1) .returns(true) CartoDB::Varnish.any_instance.expects(:purge) .with(".*#{uuid}:vizjson") .at_least_once .returns(true) doomed_user.destroy DataImport.where(user_id: doomed_user.id).count.should == 0 UserTable.where(user_id: doomed_user.id).count.should == 0 Layer.db["SELECT * from layers_users WHERE user_id = '#{doomed_user.id}'"].count.should == 0 end describe '#destroy' do it 'deletes database role' do u1 = create_user(email: 'ddr@example.com', username: 'ddr', password: 'admin123') role = u1.database_username db = u1.in_database db_service = u1.db_service db_service.role_exists?(db, role).should == true u1.destroy expect do db_service.role_exists?(db, role).should == false end.to raise_error(/role "#{role}" does not exist/) db.disconnect end it 'deletes api keys' do user = create_user(email: 'ddr@example.com', username: 'ddr', password: 'admin123') api_key = FactoryGirl.create(:api_key_apis, user_id: user.id) user.destroy expect(Carto::ApiKey.exists?(api_key.id)).to be_false expect($users_metadata.exists(api_key.send(:redis_key))).to be_false end describe "on organizations" do include_context 'organization with users helper' it 'deletes database role' do role = @org_user_1.database_username db = @org_user_1.in_database db_service = @org_user_1.db_service db_service.role_exists?(db, role).should == true @org_user_1.destroy expect do db_service.role_exists?(db, role).should == false end.to raise_error(/role "#{role}" does not exist/) db.disconnect end it 'deletes temporary analysis tables' do db = @org_user_2.in_database db.run('CREATE TABLE analysis_cd60938c7b_2ad1345b134ed3cd363c6de651283be9bd65094e (a int)') db.run(%{INSERT INTO cdb_analysis_catalog (username, cache_tables, node_id, analysis_def) VALUES ('#{@org_user_2.username}', '{analysis_cd60938c7b_2ad1345b134ed3cd363c6de651283be9bd65094e}', 'a0', '{}')}) @org_user_2.destroy db = @org_user_owner.in_database db["SELECT COUNT(*) FROM cdb_analysis_catalog WHERE username='#{@org_user_2.username}'"].first[:count].should eq 0 end describe 'User#destroy' do include TableSharing it 'blocks deletion with shared entities' do @not_to_be_deleted = TestUserFactory.new.create_test_user(unique_name('user'), @organization) table = create_random_table(@not_to_be_deleted) share_table_with_user(table, @org_user_owner) expect { @not_to_be_deleted.destroy }.to raise_error(/Cannot delete user, has shared entities/) ::User[@not_to_be_deleted.id].should be end it 'deletes api keys and associated roles' do user = TestUserFactory.new.create_test_user(unique_name('user'), @organization) api_key = FactoryGirl.create(:api_key_apis, user_id: user.id) user.destroy expect(Carto::ApiKey.exists?(api_key.id)).to be_false expect($users_metadata.exists(api_key.send(:redis_key))).to be_false expect( @org_user_owner.in_database["SELECT 1 FROM pg_roles WHERE rolname = '#{api_key.db_role}'"].first ).to be_nil end it 'deletes client_application and friends' do user = create_user(email: 'clientapp@example.com', username: 'clientapp', password: @user_password) user.create_client_application user.client_application.access_tokens << Carto::AccessToken.new( token: "access_token", secret: "access_secret", callback_url: "http://callback2", verifier: "v2", scope: nil, client_application_id: user.client_application.id ).save user.client_application.oauth_tokens << Carto::OauthToken.create!( token: "oauth_token", secret: "oauth_secret", callback_url: "http//callback.com", verifier: "v1", scope: nil, client_application_id: user.client_application.id ) base_key = "rails:oauth_access_tokens:#{user.client_application.access_tokens.first.token}" client_application = ClientApplication.where(user_id: user.id).first expect(ClientApplication.where(user_id: user.id).count).to eq 2 expect(client_application.tokens).to_not be_empty expect(client_application.tokens.length).to eq 2 $api_credentials.keys.should include(base_key) user.destroy expect(ClientApplication.where(user_id: user.id).first).to be_nil expect(Carto::AccessToken.where(user_id: user.id).first).to be_nil expect(Carto::OauthToken.where(user_id: user.id).first).to be_nil $api_credentials.keys.should_not include(base_key) end it 'deletes oauth_apps and friends' do owner_oauth_app = create_user(email: 'owner@example.com', username: 'oauthappowner', password: @user_password) user = create_user(email: 'oauth@example.com', username: 'oauthapp', password: @user_password) oauth_app = FactoryGirl.create(:oauth_app, user_id: owner_oauth_app.id) oauth_app_user = oauth_app.oauth_app_users.create!(user_id: user.id) oac = oauth_app_user.oauth_authorization_codes.create!(scopes: ['offline']) access_token, refresh_token = oac.exchange! app = Carto::OauthApp.where(user_id: owner_oauth_app.id).first users = app.oauth_app_users o_user = users.first refresh_token = Carto::OauthRefreshToken.where(oauth_app_user_id: o_user.id).first access_token = Carto::OauthAccessToken.where(oauth_app_user_id: o_user.id).first api_keys = Carto::ApiKey.where(user_id: user.id, type: 'oauth').all expect(users.count).to eq 1 expect(refresh_token).to eq refresh_token expect(access_token).to eq access_token expect(api_keys.count).to eq 1 expect(user.destroy).to be_true app = Carto::OauthApp.where(user_id: user.id).first users = Carto::OauthAppUser.where(user_id: user.id, oauth_app: oauth_app.id).first refresh_token = Carto::OauthRefreshToken.where(oauth_app_user_id: oauth_app_user.id).first access_token = Carto::OauthAccessToken.where(oauth_app_user_id: oauth_app_user.id).first api_key = Carto::ApiKey.where(user_id: user.id, type: 'oauth').first expect(app).to be_nil expect(users).to be_nil expect(refresh_token).to be_nil expect(access_token).to be_nil expect(api_key).to be_nil end end end end describe '#destroy' do def create_full_data carto_user = FactoryGirl.create(:carto_user) user = ::User[carto_user.id] table = create_table(user_id: carto_user.id, name: 'My first table', privacy: UserTable::PRIVACY_PUBLIC) canonical_visualization = table.table_visualization map = FactoryGirl.create(:carto_map_with_layers, user_id: carto_user.id) carto_visualization = FactoryGirl.create(:carto_visualization, user: carto_user, map: map) visualization = CartoDB::Visualization::Member.new(id: carto_visualization.id).fetch # Force ORM to cache layers (to check if they are deleted later) canonical_visualization.map.layers visualization.map.layers user_layer = Layer.create(kind: 'tiled') user.add_layer(user_layer) [user, table, [canonical_visualization, visualization], user_layer] end def check_deleted_data(user_id, table_id, visualizations, layer_id) ::User[user_id].should be_nil visualizations.each do |visualization| Carto::Visualization.exists?(visualization.id).should be_false visualization.map.layers.each { |layer| Carto::Layer.exists?(layer.id).should be_false } end Carto::UserTable.exists?(table_id).should be_false Carto::Layer.exists?(layer_id).should be_false end it 'destroys all related information' do user, table, visualizations, layer = create_full_data ::User[user.id].destroy check_deleted_data(user.id, table.id, visualizations, layer.id) end it 'destroys all related information, even for viewer users' do user, table, visualizations, layer = create_full_data user.viewer = true user.save user.reload user.destroy check_deleted_data(user.id, table.id, visualizations, layer.id) end end describe 'User#destroy_cascade' do include_context 'organization with users helper' include TableSharing it 'allows deletion even with shared entities' do table = create_random_table(@org_user_1) share_table_with_user(table, @org_user_1) @org_user_1.destroy_cascade ::User[@org_user_1.id].should_not be end end describe "#regressions" do it "Tests geocodings and data import FK not breaking user destruction" do user = create_user user_id = user.id data_import_id = '11111111-1111-1111-1111-111111111111' SequelRails.connection.run(%Q{ INSERT INTO data_imports("data_source","data_type","table_name","state","success","logger","updated_at", "created_at","tables_created_count", "table_names","append","id","table_id","user_id", "service_name","service_item_id","stats","type_guessing","quoted_fields_guessing","content_guessing","server","host", "resque_ppid","upload_host","create_visualization","user_defined_limits") VALUES('test','url','test','complete','t','11111111-1111-1111-1111-111111111112', '2015-03-17 00:00:00.94006+00','2015-03-17 00:00:00.810581+00','1', 'test','f','#{data_import_id}','11111111-1111-1111-1111-111111111113', '#{user_id}','public_url', 'test', '[{"type":".csv","size":5015}]','t','f','t','test','0.0.0.0','13204','test','f','{"twitter_credits_limit":0}'); }) SequelRails.connection.run(%Q{ INSERT INTO geocodings("table_name","processed_rows","created_at","updated_at","formatter","state", "id","user_id", "cache_hits","kind","geometry_type","processable_rows","real_rows","used_credits", "data_import_id" ) VALUES('importer_123456','197','2015-03-17 00:00:00.279934+00','2015-03-17 00:00:00.536383+00','field_1','finished', '11111111-1111-1111-1111-111111111114','#{user_id}','0','admin0','polygon','195','0','0', '#{data_import_id}'); }) user.destroy ::User.find(id:user_id).should eq nil end end end