cartodb-4.42/spec/models/carto/api_key_spec.rb
2024-04-06 05:25:13 +00:00

1083 lines
37 KiB
Ruby

require 'spec_helper_min'
require 'support/helpers'
require 'helpers/database_connection_helper'
describe Carto::ApiKey do
include CartoDB::Factories
include DatabaseConnectionHelper
def api_key_table_permissions(api_key, schema, table_name)
api_key.table_permissions_from_db.find do |tp|
tp.schema == schema && tp.name == table_name
end
end
def api_key_schema_permissions(api_key, schema)
api_key.schema_permissions_from_db.find do |sp|
sp.name == schema
end
end
def database_grant(database_schema = 'wadus', table_name = 'wadus',
owner = false,
permissions: ['insert', 'select', 'update', 'delete'],
schema_permissions: ['create'])
{
type: "database",
tables: [
{
schema: database_schema,
name: table_name,
owner: owner,
permissions: permissions
}
],
schemas: [
{
name: database_schema,
permissions: schema_permissions
}
]
}
end
def table_grant(database_schema = 'wadus', table_name = 'wadus', owner = false,
permissions: ['insert', 'select', 'update', 'delete'])
{
type: "database",
tables: [
{
schema: database_schema,
name: table_name,
owner: owner,
permissions: permissions
}
]
}
end
def schema_grant(database_schema = 'wadus', schema_permissions: ['create'])
{
type: "database",
schemas: [
{
name: database_schema,
permissions: schema_permissions
}
]
}
end
def apis_grant(apis = ['maps', 'sql'])
{
type: 'apis',
apis: apis
}
end
def data_services_grant(services = ['geocoding', 'routing', 'isolines', 'observatory'])
{
type: 'dataservices',
services: services
}
end
def user_grant(data = ['profile'])
{
type: 'user',
data: data
}
end
shared_examples_for 'api key' do
before(:all) do
@table1 = create_table(user_id: @carto_user1.id)
@table2 = create_table(user_id: @carto_user1.id)
@table3 = create_table(user_id: @carto_user1.id)
end
after(:all) do
bypass_named_maps
@table2.destroy
@table1.destroy
@table3.destroy
end
after(:each) do
@carto_user1.reload.api_keys.where(type: Carto::ApiKey::TYPE_REGULAR).each(&:delete)
end
it 'can grant insert, select, update delete to a database role' do
grants = [database_grant(@table1.database_schema, @table1.name), apis_grant]
api_key = @carto_user1.api_keys.create_regular_key!(name: 'full', grants: grants)
with_connection_from_api_key(api_key) do |connection|
begin
connection.execute("select count(1) from #{@table2.name}")
rescue Sequel::DatabaseError => e
failed = true
e.message.should match /permission denied .* #{@table2.name}/
end
failed.should be_true
connection.execute("select count(1) from #{@table1.name}") do |result|
result[0]['count'].should eq '0'
end
connection.execute("insert into #{@table1.name} (name) values ('wadus')")
connection.execute("select count(1) from #{@table1.name}") do |result|
result[0]['count'].should eq '1'
end
connection.execute("update #{@table1.name} set name = 'wadus2' where name = 'wadus'")
connection.execute("delete from #{@table1.name} where name = 'wadus2'")
connection.execute("select count(1) from #{@table1.name}") do |result|
result[0]['count'].should eq '0'
end
end
api_key.destroy
end
it 'can grant only with select and update permissions' do
grants = [database_grant(@table1.database_schema, @table1.name, permissions: ['select', 'update']), apis_grant]
api_key = @carto_user1.api_keys.create_regular_key!(name: 'only_update', grants: grants)
with_connection_from_api_key(api_key) do |connection|
connection.execute("select count(1) from #{@table1.name}") do |result|
result[0]['count'].should eq '0'
end
connection.execute("update #{@table1.name} set name = 'wadus2' where name = 'wadus'")
end
end
it 'grants create tables on schema' do
table1 = create_table(user_id: @carto_user1.id)
grants = [schema_grant(table1.database_schema), apis_grant]
api_key = @carto_user1.api_keys.create_regular_key!(name: 'only_update', grants: grants)
table1.destroy
with_connection_from_api_key(api_key) do |connection|
create_select_drop_check(connection, table1.database_schema, table1.name)
end
end
it 'master role is able to drop the table created by regular api with schema grants' do
grants = [schema_grant(@carto_user1.database_schema), apis_grant]
api_key = @carto_user1.api_keys.create_regular_key!(name: 'drop_by_master', grants: grants)
with_connection_from_api_key(api_key) do |connection|
create_select_drop_check(connection, @carto_user1.database_schema, 'test_table', false)
end
@carto_user1.in_database.execute("drop table test_table")
end
it 'reassigns role ownerships after deleting a regular API key' do
grants = [schema_grant(@carto_user1.database_schema), apis_grant]
api_key = @carto_user1.api_keys.create_regular_key!(name: 'drop_test', grants: grants)
with_connection_from_api_key(api_key) do |connection|
create_select_drop_check(connection, @carto_user1.database_schema, 'test_table', false)
end
api_key.destroy
ownership_query = "select pg_catalog.pg_get_userbyid(relowner) as owner from pg_class where relname = 'test_table'"
@carto_user1.in_database.execute(ownership_query) do |result|
result[0]['owner'].should eq @carto_user1.database_username
end
@carto_user1.in_database.execute("drop table test_table")
end
it 'fails to grant to a non-existent schema' do
expect {
grants = [schema_grant('not-exists'), apis_grant]
@carto_user1.api_keys.create_regular_key!(name: 'full', grants: grants)
}.to raise_exception(ActiveRecord::RecordInvalid, /can only grant schema permissions you have/)
end
it 'fails to grant to a non-existent table' do
expect {
grants = [database_grant(@carto_user1.database_schema, 'not-exists'), apis_grant]
@carto_user1.api_keys.create_regular_key!(name: 'full', grants: grants)
}.to raise_exception(ActiveRecord::RecordInvalid, /can only grant table permissions you have/)
end
it 'fails to grant to system table' do
expect {
grants = [database_grant('cartodb', 'cdb_tablemetadata'), apis_grant]
@carto_user1.api_keys.create_regular_key!(name: 'full', grants: grants)
}.to raise_exception ActiveRecord::RecordInvalid
end
it 'fails to access system tables' do
api_key = @carto_user1.api_keys.create_regular_key!(name: 'full', grants: [apis_grant])
with_connection_from_api_key(api_key) do |connection|
['cdb_tablemetadata', 'cdb_analysis_catalog'].each do |table|
expect {
connection.execute("select count(1) from cartodb.#{table}")
}.to raise_exception /permission denied/
end
end
end
it 'fails to access schemas not granted' do
api_key = @carto_user1.api_keys.create_regular_key!(name: 'full', grants: [apis_grant])
with_connection_from_api_key(api_key) do |connection|
expect {
connection.execute("create table \"#{@table1.database_schema}\".test as select 1 as test")
}.to raise_exception /permission denied/
end
end
it 'fails to grant to system schema' do
expect {
grants = [schema_grant('information_schema'), apis_grant]
@carto_user1.api_keys.create_regular_key!(name: 'full', grants: grants)
}.to raise_exception ActiveRecord::RecordInvalid
end
it 'fails to create table in system schema' do
api_key = @carto_user1.api_keys.create_regular_key!(name: 'full', grants: [apis_grant])
with_connection_from_api_key(api_key) do |connection|
expect {
connection.execute("create table information_schema.test as select 1 as test")
}.to raise_exception /permission denied/
end
end
it 'grants to a double quoted table name' do
old_name = @table3.name
@user1.in_database.run("ALTER TABLE #{old_name} RENAME TO \"wadus\"\"wadus\"")
grants = [database_grant(@carto_user1.database_schema, 'wadus"wadus'), apis_grant]
api_key = @carto_user1.api_keys.create_regular_key!(name: 'valid_table_name', grants: grants)
with_connection_from_api_key(api_key) do |connection|
connection.execute("select count(1) from \"wadus\"\"wadus\"") do |result|
result[0]['count'].should eq '0'
end
end
api_key.destroy
@user1.in_database.run("ALTER TABLE \"wadus\"\"wadus\" RENAME TO #{old_name}")
end
it 'grants view' do
view_name = 'cool_view'
validate_view_api_key(
view_name,
"CREATE VIEW #{view_name} AS SELECT * FROM #{@table1.name}",
"DROP VIEW #{view_name}"
)
validate_view_api_key(
view_name,
"CREATE MATERIALIZED VIEW #{view_name} AS SELECT * FROM #{@table1.name}",
"DROP MATERIALIZED VIEW #{view_name}"
)
end
def validate_view_api_key(view_name, create_query, drop_query)
@user1.in_database.run(create_query)
grants = [apis_grant(['sql']), database_grant(@table1.database_schema, view_name)]
api_key = @carto_user1.api_keys.create_regular_key!(name: 'grants_view', grants: grants)
with_connection_from_api_key(api_key) do |connection|
begin
connection.execute("select count(1) from #{@table1.name}")
rescue Sequel::DatabaseError => e
e.message.should match /permission denied .* #{@table1.name}/
end
connection.execute("select count(1) from #{view_name}") do |result|
result[0]['count'].should eq '0'
end
end
@user1.in_database.run(drop_query)
api_key.destroy
end
it 'show ownership of the tables for the user' do
grants = [schema_grant(@carto_user1.database_schema), apis_grant]
api_key = @carto_user1.api_keys.create_regular_key!(name: 'table_owner_test', grants: grants)
with_connection_from_api_key(api_key) do |connection|
create_select_drop_check(connection, @carto_user1.database_schema, 'test_table', false)
end
permissions = api_key.table_permissions_from_db
permissions.each do |p|
if p.name == 'test_table'
p.owner.should eq true
end
end
with_connection_from_api_key(api_key) do |connection|
connection.execute("drop table \"#{@carto_user1.database_schema}\".test_table")
end
end
it 'regular key owner with creation permission can cartodbfy tables' do
grants = [schema_grant(@carto_user1.database_schema), apis_grant]
api_key = @carto_user1.api_keys.create_regular_key!(name: 'table_owner_test', grants: grants)
with_connection_from_api_key(api_key) do |connection|
connection.execute("create table \"#{@carto_user1.database_schema}\".test_table(id int)")
connection.execute("select cdb_cartodbfytable('#{@carto_user1.database_schema}', 'test_table')")
connection.execute("insert into \"#{@carto_user1.database_schema}\".test_table(the_geom, id) values ('0103000020E610000001000000040000009A99999999C9524048E17A14AE873D4000000000004053400000000000003D4066666666666653400000000000803D409A99999999C9524048E17A14AE873D40'::geometry, 1)")
connection.execute("select * from \"#{@carto_user1.database_schema}\".test_table") do |result|
result[0]['cartodb_id'].should eq '1'
result[0]['id'].should eq '1'
end
connection.execute("drop table \"#{@carto_user1.database_schema}\".test_table")
end
end
let (:grants) { [database_grant(@table1.database_schema, @table1.name), apis_grant] }
describe '#destroy' do
it 'removes the role from DB' do
api_key = @carto_user1.api_keys.create_regular_key!(name: 'full', grants: grants)
@user1.in_database(as: :superuser) do |db|
db.fetch("SELECT count(1) FROM pg_roles WHERE rolname = '#{api_key.db_role}'").first[:count].should eq 1
end
api_key.destroy
@user1.in_database(as: :superuser) do |db|
db.fetch("SELECT count(1) FROM pg_roles WHERE rolname = '#{api_key.db_role}'").first[:count].should eq 0
end
end
it 'removes the role from Redis' do
api_key = @carto_user1.api_keys.create_regular_key!(name: 'full', grants: grants)
$users_metadata.hgetall(api_key.send(:redis_key)).should_not be_empty
api_key.destroy
$users_metadata.hgetall(api_key.send(:redis_key)).should be_empty
end
it 'invalidates varnish cache' do
CartoDB::Varnish.any_instance.expects(:purge).with("#{@user1.database_name}.*").at_least(1)
api_key = @carto_user1.api_keys.create_regular_key!(name: 'full', grants: grants)
api_key.destroy
end
end
describe '#regenerate_token!' do
it 'regenerates the value in Redis only after save' do
api_key = @carto_user1.api_keys.create_regular_key!(name: 'full', grants: grants)
old_redis_key = api_key.send(:redis_key)
$users_metadata.hgetall(old_redis_key).should_not be_empty
api_key.regenerate_token!
new_redis_key = api_key.send(:redis_key)
new_redis_key.should_not be eq old_redis_key
$users_metadata.hgetall(new_redis_key).should_not be_empty
$users_metadata.hgetall(old_redis_key).should be_empty
# Additional check that just saving doesn't change Redis
api_key.save!
$users_metadata.hgetall(new_redis_key).should_not be_empty
$users_metadata.hgetall(old_redis_key).should be_empty
end
it 'invalidates varnish cache' do
api_key = @carto_user1.api_keys.create_regular_key!(name: 'full', grants: grants)
CartoDB::Varnish.any_instance.expects(:purge).with("#{@user1.database_name}.*").at_least(1)
api_key.regenerate_token!
api_key.save!
end
end
describe 'validations' do
it 'fails with invalid schema permissions' do
database_grants = {
type: "database",
tables: [
{
schema: "wadus",
name: "wadus",
permissions: ["insert"]
}
],
schemas: [
{
name: "wadus",
permissions: ["create", "insert"]
}
]
}
grants = [apis_grant, database_grants]
expect {
@carto_user1.api_keys.create_regular_key!(name: 'x', grants: grants)
}.to raise_exception(ActiveRecord::RecordInvalid, /value "insert" did not match one of the following values/)
end
it 'validates with no tables' do
database_grants = {
type: "database"
}
grants = [apis_grant, database_grants]
expect {
@carto_user1.api_keys.create_regular_key!(name: 'x', grants: grants)
}.to_not raise_error
end
it 'validates tables_metadata grant' do
database_grants = {
type: "database",
table_metadata: []
}
grants = [apis_grant, database_grants]
expect {
@carto_user1.api_keys.create_regular_key!(name: 'x', grants: grants)
}.to_not raise_error
end
it 'validates do API grant' do
apis_grants = {
type: "apis",
apis: ["do"]
}
expect {
@carto_user1.api_keys.create_regular_key!(name: 'x', grants: [apis_grants])
}.to_not raise_error
end
it 'fails with several apis sections' do
two_apis_grant = [apis_grant, apis_grant, database_grant]
expect {
@carto_user1.api_keys.create_regular_key!(name: 'x', grants: two_apis_grant)
}.to raise_exception(ActiveRecord::RecordInvalid, /Grants only one apis section is allowed/)
end
it 'fails with several database sections' do
two_apis_grant = [apis_grant, database_grant, database_grant]
expect {
@carto_user1.api_keys.create_regular_key!(name: 'x', grants: two_apis_grant)
}.to raise_exception(ActiveRecord::RecordInvalid, /Grants only one database section is allowed/)
end
it 'fails when creating without apis grants' do
grants = JSON.parse('
[
{
"type": "database",
"tables": [{
"name": "something",
"schema": "public",
"permissions": [
"select"
]
},
{
"name": "another",
"schema": "public",
"permissions": ["insert", "update", "select"]
}
]
}
]', symbolize_names: true)
expect {
@carto_user1.api_keys.create_regular_key!(name: 'x', grants: grants)
}.to raise_exception(ActiveRecord::RecordInvalid, /Grants only one apis section is allowed/)
end
it 'fails with several dataservices sections' do
two_apis_grant = [apis_grant, data_services_grant, data_services_grant]
expect {
@carto_user1.api_keys.create_regular_key!(name: 'x', grants: two_apis_grant)
}.to raise_exception(ActiveRecord::RecordInvalid, /Grants only one dataservices section is allowed/)
end
it 'fails with several user sections' do
two_apis_grant = [apis_grant, user_grant, user_grant]
expect {
@carto_user1.api_keys.create_regular_key!(name: 'x', grants: two_apis_grant)
}.to raise_exception(ActiveRecord::RecordInvalid, /Grants only one user section is allowed/)
end
context 'without enough regular api key quota' do
before(:all) do
@carto_user1.regular_api_key_quota = 0
@carto_user1.save
end
after(:all) do
@carto_user1.regular_api_key_quota = be_nil
@carto_user1.save
end
it 'raises an exception when creating a regular key' do
grants = [database_grant(@table1.database_schema, @table1.name), apis_grant]
expect {
@carto_user1.api_keys.create_regular_key!(name: 'full', grants: grants)
}.to raise_exception(CartoDB::QuotaExceeded, /limit of API keys/)
end
end
end
describe '#table_permission_from_db' do
before(:all) do
@public_table = create_table(user_id: @carto_user1.id)
@public_table.table_visualization.update_attributes(privacy: 'public')
end
after(:all) do
@public_table.destroy
end
it 'loads newly created grants for role' do
grants = [database_grant(@user1.database_schema, @table1.name), apis_grant(['maps', 'sql'])]
api_key = @carto_user1.api_keys.create_regular_key!(name: 'wadus', grants: grants)
sql = "grant SELECT on table \"#{@table2.database_schema}\".\"#{@table2.name}\" to \"#{api_key.db_role}\""
@user1.in_database(as: :superuser).run(sql)
table_permission = api_key_table_permissions(api_key, @table2.database_schema, @table2.name)
table_permission.should be
table_permission.permissions.should include('select')
api_key.destroy
end
it 'doesn\'t show removed table' do
permissions = ['insert', 'select', 'update', 'delete']
grants = [database_grant(@user1.database_schema, @table1.name, permissions: permissions), apis_grant]
api_key = @carto_user1.api_keys.create_regular_key!(name: 'wadus', grants: grants)
permissions.each do |permission|
api_key_table_permissions(api_key, @table1.database_schema, @table1.name).permissions.should include(permission)
end
sql = "drop table \"#{@user1.database_schema}\".\"#{@table1.name}\""
@user1.in_database(as: :superuser).run(sql)
api_key_table_permissions(api_key, @table1.database_schema, @table1.name).should be_nil
api_key.destroy
end
it 'shows public tables' do
api_key = @carto_user1.api_keys.default_public.first
unless @carto_user1.has_organization?
api_key_table_permissions(api_key, @public_table.database_schema, @public_table.name)
.permissions.should eq ['select']
end
end
end
describe '#schema_permission_from_db' do
before(:all) do
@public_table = create_table(user_id: @carto_user1.id)
end
after(:all) do
@public_table.destroy
end
it 'loads newly created grants for role' do
schema_name = 'test'
grants = [apis_grant(['maps', 'sql'])]
api_key = @carto_user1.api_keys.create_regular_key!(name: 'wadus', grants: grants)
schema_permission = api_key_schema_permissions(api_key, schema_name)
schema_permission.should be_nil
create_schema
sql = "GRANT CREATE ON SCHEMA \"#{schema_name}\" to \"#{api_key.db_role}\""
@user1.in_database(as: :superuser).run(sql)
schema_permission = api_key_schema_permissions(api_key, schema_name)
schema_permission.should be
schema_permission.permissions.should include('create')
drop_schema
api_key.destroy
end
it 'doesn\'t show removed schema' do
schema_name = 'test'
create_schema
grant_user
api_key = create_api_key
permissions = ['create']
permissions.each do |permission|
api_key_schema_permissions(api_key, schema_name).permissions.should include(permission)
end
drop_schema
api_key_schema_permissions(api_key, schema_name).should be_nil
api_key.destroy
end
it 'grants creation in schema to master role' do
schema_name = 'test'
create_schema
grant_user
api_key = create_api_key
master_api_key = @carto_user1.api_keys.master.first
permissions = ['create']
permissions.each do |permission|
api_key_schema_permissions(master_api_key, schema_name).permissions.should include(permission)
end
drop_schema
api_key_schema_permissions(master_api_key, schema_name).should be_nil
api_key.destroy
end
it 'shows public schemas' do
api_key = @carto_user1.api_keys.default_public.first
unless @carto_user1.has_organization?
api_key_schema_permissions(api_key, @public_table.database_schema)
.permissions.should eq ['usage']
end
end
end
describe 'master api key' do
it 'user has a master key with the user db_role' do
api_key = @carto_user1.api_keys.master.first
api_key.should be
api_key.db_role.should eq @carto_user1.database_username
api_key.db_password.should eq @carto_user1.database_password
end
it 'cannot create more than one master key' do
expect {
@carto_user1.api_keys.create_master_key!
}.to raise_error(ActiveRecord::RecordInvalid)
end
it 'create master api key works' do
api_key = @carto_user1.api_keys.master.first
api_key.destroy
@carto_user1.api_keys.create_master_key!
api_key = @carto_user1.api_keys.master.first
api_key.should be
api_key.db_role.should eq @carto_user1.database_username
api_key.db_password.should eq @carto_user1.database_password
end
it 'cannot create a non master api_key with master as the name' do
expect {
@carto_user1.api_keys.create_regular_key!(name: Carto::ApiKey::NAME_MASTER, grants: [apis_grant])
}.to raise_error(ActiveRecord::RecordInvalid)
end
it 'token must match user api key' do
api_key = @carto_user1.api_keys.master.first
api_key.token = 'wadus'
api_key.save.should be_false
api_key.errors.full_messages.should include "Token must match user model for master keys"
end
end
describe 'default public api key' do
it 'user has a default public key with the public_db_user role' do
api_key = @carto_user1.api_keys.default_public.first
api_key.should be
api_key.db_role.should eq @carto_user1.database_public_username
api_key.db_password.should eq CartoDB::PUBLIC_DB_USER_PASSWORD
end
it 'cannot create more than one default public key' do
expect {
@carto_user1.api_keys.create_default_public_key!
}.to raise_error(ActiveRecord::RecordInvalid)
end
it 'cannot create a non default public api_key with default public name' do
expect {
@carto_user1.api_keys.create_regular_key!(name: Carto::ApiKey::NAME_DEFAULT_PUBLIC, grants: [apis_grant])
}.to raise_error(ActiveRecord::RecordInvalid)
end
it 'cannot change token' do
api_key = @carto_user1.api_keys.default_public.first
api_key.token = 'wadus'
api_key.save.should be_false
api_key.errors.full_messages.should include "Token must be default_public for default public keys"
end
end
describe 'data services api key' do
before :each do
@db_role = Carto::DB::Sanitize.sanitize_identifier("carto_role_#{SecureRandom.hex}")
Carto::ApiKey.any_instance.stubs(:db_role).returns(@db_role)
end
after :each do
Carto::ApiKey.any_instance.unstub(:db_role)
end
it 'cdb_conf info with dataservices' do
grants = [apis_grant, data_services_grant]
api_key = @carto_user1.api_keys.create_regular_key!(name: 'dataservices', grants: grants)
expected = { username: @carto_user1.username,
permissions: ['geocoding', 'routing', 'isolines', 'observatory'],
ownership_role_name: '' }
@user1.in_database(as: :superuser) do |db|
query = "SELECT cartodb.cdb_conf_getconf('#{Carto::ApiKey::CDB_CONF_KEY_PREFIX}#{api_key.db_role}')"
config = db.fetch(query).first[:cdb_conf_getconf]
expect(JSON.parse(config, symbolize_names: true)).to eql(expected)
end
api_key.destroy
@user1.in_database(as: :superuser) do |db|
query = "SELECT cartodb.cdb_conf_getconf('#{Carto::ApiKey::CDB_CONF_KEY_PREFIX}#{api_key.db_role}')"
db.fetch(query).first[:cdb_conf_getconf].should be_nil
end
end
it 'cdb_conf info without dataservices' do
grants = [apis_grant]
api_key = @carto_user1.api_keys.create_regular_key!(name: 'testname', grants: grants)
expected = { username: @carto_user1.username, permissions: [], ownership_role_name: '' }
@user1.in_database(as: :superuser) do |db|
query = "SELECT cartodb.cdb_conf_getconf('#{Carto::ApiKey::CDB_CONF_KEY_PREFIX}#{api_key.db_role}')"
config = db.fetch(query).first[:cdb_conf_getconf]
expect(JSON.parse(config, symbolize_names: true)).to eql(expected)
end
api_key.destroy
@user1.in_database(as: :superuser) do |db|
query = "SELECT cartodb.cdb_conf_getconf('#{Carto::ApiKey::CDB_CONF_KEY_PREFIX}#{api_key.db_role}')"
db.fetch(query).first[:cdb_conf_getconf].should be_nil
end
end
it 'fails with invalid data services' do
grants = [apis_grant, data_services_grant(['invalid-service'])]
expect {
@carto_user1.api_keys.create_regular_key!(name: 'bad', grants: grants)
}.to raise_error(ActiveRecord::RecordInvalid)
end
it 'fails with valid and invalid data services' do
grants = [apis_grant, data_services_grant(services: ['geocoding', 'invalid-service'])]
expect {
@carto_user1.api_keys.create_regular_key!(name: 'bad', grants: grants)
}.to raise_error(ActiveRecord::RecordInvalid)
end
it 'fails with invalid data services key' do
grants = [
apis_grant,
{
type: 'dataservices',
invalid: ['geocoding']
}
]
expect {
@carto_user1.api_keys.create_regular_key!(name: 'bad', grants: grants)
}.to raise_error(ActiveRecord::RecordInvalid)
end
end
describe 'filter by type' do
it 'filters just master' do
api_keys = @carto_user1.api_keys.by_type([Carto::ApiKey::TYPE_MASTER])
api_keys.count.should eq 1
api_keys.first.type.should eq Carto::ApiKey::TYPE_MASTER
end
it 'filters just default_public' do
api_keys = @carto_user1.api_keys.by_type([Carto::ApiKey::TYPE_DEFAULT_PUBLIC])
api_keys.count.should eq 1
api_keys.first.type.should eq Carto::ApiKey::TYPE_DEFAULT_PUBLIC
end
it 'filters default_public and master' do
api_keys = @carto_user1.api_keys.by_type([Carto::ApiKey::TYPE_DEFAULT_PUBLIC, Carto::ApiKey::TYPE_MASTER])
api_keys.count.should eq 2
end
it 'filters all if empty array' do
api_keys = @carto_user1.api_keys.by_type([])
api_keys.count.should eq 2
end
it 'filters all if nil type' do
api_keys = @carto_user1.api_keys.by_type(nil)
api_keys.count.should eq 2
end
end
describe '#data_observatory_permissions?' do
it 'returns true when it has the do api grant' do
apis_grants = {
type: "apis",
apis: ["do"]
}
api_key = @carto_user1.api_keys.create_regular_key!(name: 'x', grants: [apis_grants])
expect(api_key.data_observatory_permissions?).to eq true
end
it 'returns false when it does not have the do api grant' do
apis_grants = {
type: "apis",
apis: ["sql"]
}
api_key = @carto_user1.api_keys.create_regular_key!(name: 'x', grants: [apis_grants])
expect(api_key.data_observatory_permissions?).to eq false
end
end
end
describe 'with plain users' do
before(:all) do
@user1 = FactoryGirl.create(:valid_user, private_tables_enabled: true, private_maps_enabled: true)
@carto_user1 = Carto::User.find(@user1.id)
end
after(:all) do
@user1.destroy
end
it_behaves_like 'api key'
end
describe 'with organization users' do
before(:all) do
@auth_organization = FactoryGirl.create(:organization, quota_in_bytes: 1.gigabytes)
@user1 = TestUserFactory.new.create_owner(@auth_organization)
@carto_user1 = Carto::User.find(@user1.id)
end
after(:all) do
@user1.destroy
@auth_organization.destroy
end
it_behaves_like 'api key'
it 'fails to grant to a non-owned table' do
other_user = TestUserFactory.new.create_test_user(unique_name('user'), @auth_organization)
table = create_table(user_id: other_user.id)
grants = [table_grant(table.database_schema, table.name), apis_grant]
expect {
@carto_user1.api_keys.create_regular_key!(name: 'full', grants: grants)
}.to raise_exception ActiveRecord::RecordInvalid
table.destroy
other_user.destroy
end
it 'fails to grant to a non-owned schema' do
other_user = TestUserFactory.new.create_test_user(unique_name('user'), @auth_organization)
table = create_table(user_id: other_user.id)
grants = [schema_grant(table.database_schema), apis_grant]
expect {
@carto_user1.api_keys.create_regular_key!(name: 'full', grants: grants)
}.to raise_exception ActiveRecord::RecordInvalid
table.destroy
other_user.destroy
end
it 'drop role with grants of objects owned by other user' do
user2 = TestUserFactory.new.create_test_user(unique_name('user'), @auth_organization)
table_user2 = create_table(user_id: user2.id)
schema_and_table_user2 = "\"#{table_user2.database_schema}\".#{table_user2.name}"
table_user1 = create_table(user_id: @carto_user1.id)
grants = [table_grant(table_user1.database_schema, table_user1.name), apis_grant]
api_key = @carto_user1.api_keys.create_regular_key!(name: 'full', grants: grants)
user2.in_database.run("GRANT SELECT ON #{schema_and_table_user2} TO \"#{api_key.db_role}\"")
expect { api_key.destroy! }.to_not raise_error
table_user1.destroy
table_user2.destroy
user2.destroy
end
it 'reassigns role ownerships after deleting an OAuth API key' do
create_schema
create_role
grant_user
api_key = create_oauth_api_key
with_connection_from_api_key(api_key) do |connection|
connection.execute("create table test.wadus()")
end
api_key.destroy
ownership_query = "select pg_catalog.pg_get_userbyid(relowner) as owner from pg_class where relname = 'wadus'"
@carto_user1.in_database.execute(ownership_query) do |result|
result[0]['owner'].should eq @carto_user1.database_username
end
@carto_user1.in_database(as: :superuser).execute("drop table test.wadus")
end
end
describe 'org shared tables' do
include_context 'organization with users helper'
before :each do
@shared_table = create_table(user_id: @carto_org_user_1.id)
perm = @shared_table.table_visualization.permission
perm.acl = [{ type: 'user', entity: { id: @carto_org_user_2.id }, access: 'rw' }]
perm.save!
end
it 'should create an api key using a shared table' do
grants = [apis_grant(['sql']), table_grant(@shared_table.database_schema, @shared_table.name)]
api_key = @carto_org_user_2.api_keys.create_regular_key!(name: 'grants_shared', grants: grants)
schema_table = "\"#{@shared_table.database_schema}\".\"#{@shared_table.name}\""
with_connection_from_api_key(api_key) do |connection|
connection.execute("select count(1) from #{schema_table}") do |result|
result[0]['count'].should eq '0'
end
end
api_key.destroy
end
it 'should revoke permissions removing shared permissions (rw to r)' do
grants = [apis_grant(['sql']), table_grant(@shared_table.database_schema, @shared_table.name)]
api_key = @carto_org_user_2.api_keys.create_regular_key!(name: 'grants_shared', grants: grants)
# remove shared permissions
@shared_table.table_visualization.reload
perm = @shared_table.table_visualization.permission
perm.acl = [{ type: 'user', entity: { id: @carto_org_user_2.id }, access: 'r' }]
perm.save!
schema_table = "\"#{@shared_table.database_schema}\".\"#{@shared_table.name}\""
with_connection_from_api_key(api_key) do |connection|
connection.execute("select count(1) from #{schema_table}") do |result|
result[0]['count'].should eq '0'
end
expect {
connection.execute("insert into #{schema_table} (name) values ('wadus')")
}.to raise_exception /permission denied/
end
api_key.destroy
end
it 'should revoke permissions removing shared permissions (rw to none)' do
grants = [apis_grant(['sql']), table_grant(@shared_table.database_schema, @shared_table.name)]
api_key = @carto_org_user_2.api_keys.create_regular_key!(name: 'grants_shared', grants: grants)
# remove shared permissions
@shared_table.table_visualization.reload
perm = @shared_table.table_visualization.permission
perm.acl = []
perm.save!
schema_table = "\"#{@shared_table.database_schema}\".\"#{@shared_table.name}\""
with_connection_from_api_key(api_key) do |connection|
expect {
connection.execute("select count(1) from #{schema_table}")
}.to raise_exception /permission denied/
expect {
connection.execute("insert into #{schema_table} (name) values ('wadus')")
}.to raise_exception /permission denied/
end
api_key.destroy
end
end
def create_schema(schema_name = 'test')
drop_schema
create_function = '
CREATE FUNCTION test._CDB_UserQuotaInBytes() RETURNS integer AS $$
BEGIN
RETURN 1;
END; $$
LANGUAGE PLPGSQL;
'
@carto_user1.in_database(as: :superuser).execute("CREATE SCHEMA \"#{schema_name}\"")
@carto_user1.in_database(as: :superuser).execute(create_function)
end
def create_role(role_name = 'test')
drop_role
@carto_user1.in_database(as: :superuser).execute("CREATE ROLE \"#{role_name}\"")
end
def drop_role(role_name = 'test')
@carto_user1.in_database(as: :superuser).execute("DROP ROLE IF EXISTS \"#{role_name}\"")
end
def grant_user(schema_name = 'test')
sql = "GRANT CREATE ON SCHEMA \"#{schema_name}\" to \"#{@carto_user1.database_username}\""
@carto_user1.in_database(as: :superuser).execute(sql)
end
def create_api_key(schema_name = 'test', permissions = ['create'])
grants = [schema_grant(schema_name, schema_permissions: permissions), apis_grant]
@carto_user1.api_keys.create_regular_key!(name: 'wadus', grants: grants)
end
def create_oauth_api_key(schema_name = 'test', permissions = ['create'], role = 'test')
grants = [schema_grant(schema_name, schema_permissions: permissions), apis_grant]
@carto_user1.api_keys.create_oauth_key!(name: 'wadus', grants: grants, ownership_role_name: role)
end
def drop_schema(schema_name = 'test')
sql = "DROP SCHEMA IF EXISTS \"#{schema_name}\" CASCADE"
@carto_user1.in_database(as: :superuser).execute(sql)
end
def create_select_drop_check(connection, schema, table_name, drop = true)
connection.execute("create table \"#{schema}\".#{table_name} as select 1 as test")
connection.execute("select count(1) from \"#{schema}\".#{table_name}") do |result|
result[0]['count'].should eq '1'
end
connection.execute("drop table \"#{schema}\".#{table_name}") if drop
end
end