cartodb/spec/models/carto/oauth_access_token_spec.rb
2020-06-15 10:58:47 +08:00

610 lines
22 KiB
Ruby

require 'spec_helper_min'
require 'helpers/database_connection_helper'
module Carto
describe OauthAccessToken do
include_context 'organization with users helper'
include DatabaseConnectionHelper
describe '#validation' do
before(:all) do
@user = FactoryGirl.create(:valid_user)
@carto_user = Carto::User.find(@user.id)
@app = FactoryGirl.create(:oauth_app, user: @carto_user)
@app_user = OauthAppUser.create!(user: @carto_user, oauth_app: @app)
@user_table = FactoryGirl.create(:carto_user_table, :with_db_table, user_id: @carto_user.id)
end
after(:all) do
@user.destroy
@app.destroy
@user_table.destroy
end
it 'does not accept invalid scopes' do
access_token = OauthAccessToken.new(oauth_app_user: @app_user, scopes: ['wadus'])
expect(access_token).to_not(be_valid)
expect(access_token.errors[:scopes]).to(include("contains unsupported scopes: wadus"))
end
it 'does accept create tables in schema scopes' do
access_token = OauthAccessToken.new(oauth_app_user: @app_user, scopes: ["schemas:c"])
expect(access_token).to(be_valid)
end
it 'does accept read datasets scopes' do
access_token = OauthAccessToken.new(oauth_app_user: @app_user, scopes: ["datasets:r:#{@user_table.name}"])
expect(access_token).to(be_valid)
end
it 'does accept read and write datasets scopes' do
access_token = OauthAccessToken.new(oauth_app_user: @app_user, scopes: ["datasets:rw:#{@user_table.name}"])
expect(access_token).to(be_valid)
end
it 'does not accept invalid permission in datasets scopes' do
access_token = OauthAccessToken.new(oauth_app_user: @app_user, scopes: ['datasets:f:wadus'])
expect(access_token).to_not(be_valid)
end
it 'does not validate tables existence on datasets scopes' do
access_token = OauthAccessToken.new(oauth_app_user: @app_user, scopes: ['datasets:r:wadus'])
expect(access_token).to(be_valid)
end
it 'does not validate schemas existence on datasets scopes' do
access_token = OauthAccessToken.new(oauth_app_user: @app_user, scopes: ['schemas:c:wadus'])
expect(access_token).to(be_valid)
end
it 'does not accept invalid datasets scopes' do
access_token = OauthAccessToken.new(oauth_app_user: @app_user, scopes: ['datasets:f'])
expect(access_token).to_not(be_valid)
end
it 'does not accept invalid schema scopes' do
access_token = OauthAccessToken.new(oauth_app_user: @app_user, scopes: ['schemas:f'])
expect(access_token).to_not(be_valid)
end
it 'auto generates api_key' do
access_token = OauthAccessToken.create!(oauth_app_user: @app_user)
expect(access_token.api_key).to(be)
expect(access_token.api_key.type).to(eq('oauth'))
end
it 'api key includes permissions for requested scopes' do
access_token = OauthAccessToken.create!(oauth_app_user: @app_user,
scopes: ['dataservices:geocoding', 'user:profile'])
expect(access_token.api_key).to(be)
expect(access_token.api_key.type).to(eq('oauth'))
expect(access_token.api_key.grants).to(include(type: 'apis', apis: ['sql']))
expect(access_token.api_key.grants).to(include(type: 'dataservices', services: ['geocoding']))
expect(access_token.api_key.grants).to(include(type: 'user', data: ['profile']))
end
it 'raises an error when creating an api key for an non-existent dataset' do
expect {
OauthAccessToken.create!(oauth_app_user: @app_user,
scopes: ['datasets:r:wadus'])
}.to raise_error(ActiveRecord::RecordInvalid)
end
it 'raises an error when creating an api key for an non-existent dataset' do
expect {
OauthAccessToken.create!(oauth_app_user: @app_user,
scopes: ['schemas:c:wadus'])
}.to raise_error(ActiveRecord::RecordInvalid)
end
it 'includes create permission for schemas scopes' do
FactoryGirl.create(:carto_user_table, :with_db_table, user_id: @user.id)
expected_grants =
[
{
type: 'apis',
apis: ['sql']
},
{
type: 'database',
schemas: [
{
name: 'public',
permissions: ['create']
}
]
}
]
access_token = OauthAccessToken.create!(oauth_app_user: @app_user,
scopes: ['schemas:c'])
expect(access_token.api_key).to(be)
expect(access_token.api_key.type).to(eq('oauth'))
expect(access_token.api_key.grants).to(eq(expected_grants))
end
it 'includes listing metadata permission for datasets scopes' do
FactoryGirl.create(:carto_user_table, :with_db_table, user_id: @user.id)
expected_grants =
[
{
type: 'apis',
apis: ['sql']
},
{
type: 'database',
table_metadata: []
}
]
access_token = OauthAccessToken.create!(oauth_app_user: @app_user, scopes: ['datasets:metadata'])
expect(access_token.api_key.type).to(eq('oauth'))
expect(access_token.api_key.grants).to(eq(expected_grants))
end
it 'api key includes read permissions for datasets scopes' do
user_table = FactoryGirl.create(:carto_user_table, :with_db_table, user_id: @user.id)
expected_grants =
[
{
type: 'apis',
apis: ['maps', 'sql']
},
{
type: 'database',
tables: [
{
name: user_table.name,
permissions: ['select'],
schema: 'public'
}
]
}
]
access_token = OauthAccessToken.create!(oauth_app_user: @app_user,
scopes: ["datasets:r:#{user_table.name}"])
expect(access_token.api_key).to(be)
expect(access_token.api_key.type).to(eq('oauth'))
expect(access_token.api_key.grants).to(eq(expected_grants))
end
it 'api key includes read-write permissions for datasets scopes' do
user_table = FactoryGirl.create(:carto_user_table, :with_db_table, user_id: @user.id)
expected_grants =
[
{
type: 'apis',
apis: ['maps', 'sql']
},
{
type: 'database',
tables: [
{
name: user_table.name,
permissions: ['select', 'insert', 'update', 'delete'],
schema: 'public'
}
]
}
]
access_token = OauthAccessToken.create!(oauth_app_user: @app_user,
scopes: ["datasets:rw:#{user_table.name}"])
expect(access_token.api_key).to(be)
expect(access_token.api_key.type).to(eq('oauth'))
expect(access_token.api_key.grants).to(eq(expected_grants))
end
it 'api key includes permissions for several datasets scopes' do
user_table = FactoryGirl.create(:carto_user_table, :with_db_table, user_id: @user.id)
user_table2 = FactoryGirl.create(:carto_user_table, :with_db_table, user_id: @user.id)
expected_grants =
[
{
type: 'apis',
apis: ['maps', 'sql']
},
{
type: 'database',
tables: [
{
name: user_table.name,
permissions: ['select'],
schema: 'public'
},
{
name: user_table2.name,
permissions: ['select', 'insert', 'update', 'delete'],
schema: 'public'
}
]
}
]
access_token = OauthAccessToken.create!(oauth_app_user: @app_user,
scopes: [
"datasets:r:#{user_table.name}",
"datasets:rw:#{user_table2.name}"
])
expect(access_token.api_key).to(be)
expect(access_token.api_key.type).to(eq('oauth'))
expect(access_token.api_key.grants).to(eq(expected_grants))
end
it 'includes service account permission for user scopes' do
FactoryGirl.create(:carto_user_table, :with_db_table, user_id: @user.id)
expected_grants =
[
{
type: 'apis',
apis: ['do']
}
]
access_token = OauthAccessToken.create!(oauth_app_user: @app_user, scopes: ['apis:do'])
expect(access_token.api_key.type).to(eq('oauth'))
expect(access_token.api_key.grants).to(eq(expected_grants))
end
end
describe 'cdb_conf_info' do
before(:all) do
@user = FactoryGirl.create(:valid_user)
@carto_user = Carto::User.find(@user.id)
@app = FactoryGirl.create(:oauth_app, user: @carto_user)
@app_user = OauthAppUser.create!(user: @carto_user, oauth_app: @app)
@user_table = FactoryGirl.create(:carto_user_table, :with_db_table, user_id: @carto_user.id)
@db_role = Carto::DB::Sanitize.sanitize_identifier("carto_role_#{SecureRandom.hex}")
Carto::ApiKey.any_instance.stubs(:db_role).returns(@db_role)
end
after(:all) do
@user.destroy
@app.destroy
@user_table.destroy
Carto::ApiKey.any_instance.unstub(:db_role)
end
it 'saves ownership_role_name in cdb_conf_info if schemas granted' do
Carto::ApiKey.any_instance.expects(:cdb_conf_info)
.returns(username: @app_user.user.username,
permissions: [],
ownership_role_name: @app_user.ownership_role_name)
.at_least_once
OauthAccessToken.create!(oauth_app_user: @app_user,
scopes: [
"schemas:c"
])
end
it 'does not save ownership_role_name in cdb_conf_info if schemas not granted' do
Carto::ApiKey.any_instance.expects(:cdb_conf_info)
.returns(username: @app_user.user.username,
permissions: [],
ownership_role_name: '')
.at_least_once
OauthAccessToken.create!(oauth_app_user: @app_user,
scopes: [
"datasets:r:#{@user_table.name}"
])
end
end
describe '#shared datasets' do
before :each do
@app = FactoryGirl.create(:oauth_app, user: @carto_org_user_1)
@app_user = OauthAppUser.create!(user: @carto_org_user_2, oauth_app: @app)
@shared_table = create_table(user_id: @carto_org_user_1.id)
@not_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!
@shared_dataset_scope = "datasets:rw:#{@carto_org_user_1.database_schema}.#{@shared_table.name}"
@non_shared_dataset_scope = "datasets:rw:#{@carto_org_user_1.database_schema}.#{@not_shared_table.name}"
end
after :each do
@app_user.destroy
@app.destroy
end
it 'works with shared dataset' do
expected_grants =
[
{
type: 'apis',
apis: ['maps', 'sql']
},
{
type: 'database',
tables: [
{
name: @shared_table.name,
permissions: ['select', 'insert', 'update', 'delete'],
schema: @carto_org_user_1.database_schema
}
]
}
]
access_token = OauthAccessToken.create!(oauth_app_user: @app_user, scopes: [@shared_dataset_scope])
expect(access_token.api_key).to(be)
expect(access_token.api_key.type).to(eq('oauth'))
expect(access_token.api_key.grants).to(eq(expected_grants))
with_connection_from_api_key(access_token.api_key) do |connection|
connection.execute("select count(1) from #{@carto_org_user_1.database_schema}.#{@shared_table.name}")
end
end
it 'should fail with non shared dataset' do
expect {
OauthAccessToken.create!(oauth_app_user: @app_user, scopes: [@non_shared_dataset_scope])
}.to raise_error(ActiveRecord::RecordInvalid, /can only grant table permissions you have/)
end
it 'should fail with non shared schema' do
expect {
OauthAccessToken.create!(oauth_app_user: @app_user, scopes: ['schemas:c:non_existent'])
}.to raise_error(ActiveRecord::RecordInvalid, /can only grant schema permissions you have/)
end
it 'should fail with shared and non shared dataset' do
expect {
OauthAccessToken.create!(
oauth_app_user: @app_user,
scopes: [@shared_dataset_scope, @non_shared_dataset_scope]
)
}.to raise_error(ActiveRecord::RecordInvalid, /can only grant table permissions you have/)
end
describe 'read - write permissions' do
before :each do
@only_read_table = create_table(user_id: @carto_org_user_1.id)
perm = @only_read_table.table_visualization.permission
perm.acl = [{ type: 'user', entity: { id: @carto_org_user_2.id }, access: 'r' }]
perm.save!
end
after :each do
@only_read_table.destroy
end
it 'should fail write scope in shared dataset with only read perms' do
rw_scope = "datasets:rw:#{@carto_org_user_1.database_schema}.#{@only_read_table.name}"
expect {
OauthAccessToken.create!(oauth_app_user: @app_user, scopes: [rw_scope])
}.to raise_error(ActiveRecord::RecordInvalid, /can only grant table permissions you have/)
end
end
end
describe 'organization shared datasets' do
before :each do
@app = FactoryGirl.create(:oauth_app, user: @carto_org_user_1)
@app_user = OauthAppUser.create!(user: @carto_org_user_2, oauth_app: @app)
@org_shared_table = create_table(user_id: @carto_org_user_1.id)
@non_org_shared_table = create_table(user_id: @carto_org_user_1.id)
perm = @org_shared_table.table_visualization.permission
perm.acl = [
{
type: Permission::TYPE_ORGANIZATION,
entity: { id: @carto_organization.id },
access: Permission::ACCESS_READWRITE
}
]
perm.save!
@org_shared_dataset_scope = "datasets:rw:#{@carto_org_user_1.database_schema}.#{@org_shared_table.name}"
@non_org_shared_dataset_scope = "datasets:rw:#{@carto_org_user_1.database_schema}.#{@non_org_shared_table.name}"
end
after :each do
@app_user.destroy
@app.destroy
end
it 'works with shared dataset' do
expected_grants =
[
{
type: 'apis',
apis: ['maps', 'sql']
},
{
type: 'database',
tables: [
{
name: @org_shared_table.name,
permissions: ['select', 'insert', 'update', 'delete'],
schema: @carto_org_user_1.database_schema
}
]
}
]
access_token = OauthAccessToken.create!(oauth_app_user: @app_user, scopes: [@org_shared_dataset_scope])
expect(access_token.api_key).to(be)
expect(access_token.api_key.type).to(eq('oauth'))
expect(access_token.api_key.grants).to(eq(expected_grants))
end
it 'works with shared schema' do
expected_grants =
[
{
type: 'apis',
apis: ['sql']
},
{
type: 'database',
schemas: [
{
name: @carto_org_user_2.database_schema,
permissions: ['create']
}
]
}
]
access_token = OauthAccessToken.create!(oauth_app_user: @app_user,
scopes: ["schemas:c:#{@carto_org_user_2.database_schema}"])
expect(access_token.api_key).to(be)
expect(access_token.api_key.type).to(eq('oauth'))
expect(access_token.api_key.grants).to(eq(expected_grants))
end
it 'should fail with non shared dataset' do
expect {
OauthAccessToken.create!(oauth_app_user: @app_user, scopes: [@non_org_shared_dataset_scope])
}.to raise_error(ActiveRecord::RecordInvalid, /can only grant table permissions you have/)
end
it 'should fail with non shared schema' do
expect {
OauthAccessToken.create!(oauth_app_user: @app_user,
scopes: ["schemas:c:wadus"])
}.to raise_error(ActiveRecord::RecordInvalid, /can only grant schema permissions you have/)
end
it 'should fail with shared and non shared dataset' do
expect {
OauthAccessToken.create!(
oauth_app_user: @app_user,
scopes: [@org_shared_dataset_scope, @non_org_shared_dataset_scope]
)
}.to raise_error(ActiveRecord::RecordInvalid, /can only grant table permissions you have/)
end
describe 'read - write permissions' do
before :each do
@only_read_table = create_table(user_id: @carto_org_user_1.id)
perm = @only_read_table.table_visualization.permission
perm.acl = [
{
type: Permission::TYPE_ORGANIZATION,
entity: { id: @carto_organization.id },
access: Permission::ACCESS_READONLY
}
]
perm.save!
end
after :each do
@only_read_table.destroy
end
it 'should fail write scope in shared dataset with only read perms' do
rw_scope = "datasets:rw:#{@carto_org_user_1.database_schema}.#{@only_read_table.name}"
expect {
OauthAccessToken.create!(oauth_app_user: @app_user, scopes: [rw_scope])
}.to raise_error(ActiveRecord::RecordInvalid, /can only grant table permissions you have/)
end
end
end
describe 'views' do
before :all do
@user = FactoryGirl.create(:valid_user)
@carto_user = Carto::User.find(@user.id)
@user_table = create_table(user_id: @carto_user.id)
@view_name = "#{@user_table.name}_view"
@materialized_view_name = "#{@user_table.name}_matview"
@carto_user.in_database do |db|
query = %{
CREATE VIEW #{@view_name} AS SELECT * FROM #{@user_table.name};
CREATE MATERIALIZED VIEW #{@materialized_view_name} AS SELECT * FROM #{@user_table.name};
}
db.execute(query)
end
end
before :each do
@app = FactoryGirl.create(:oauth_app, user: @carto_user)
@app_user = OauthAppUser.create!(user: @carto_user, oauth_app: @app)
end
after :each do
@app_user.destroy
@app.destroy
end
after :all do
@carto_user.in_database do |db|
query = %{
DROP VIEW #{@view_name};
DROP MATERIALIZED VIEW #{@materialized_view_name};
}
db.execute(query)
end
@user_table.destroy
@user.destroy
@carto_user.destroy
end
it 'validates view scope' do
expected_grants =
[
{
type: 'apis',
apis: ['maps', 'sql']
},
{
type: 'database',
tables: [
{
name: @view_name,
permissions: ['select', 'insert', 'update', 'delete'],
schema: @carto_user.database_schema
}
]
}
]
access_token = OauthAccessToken.create!(oauth_app_user: @app_user, scopes: ["datasets:rw:#{@view_name}"])
expect(access_token.api_key).to(be)
expect(access_token.api_key.type).to(eq('oauth'))
expect(access_token.api_key.grants).to(eq(expected_grants))
end
it 'validates materialized view scope' do
expected_grants =
[
{
type: 'apis',
apis: ['maps', 'sql']
},
{
type: 'database',
tables: [
{
name: @materialized_view_name,
permissions: ['select', 'insert', 'update', 'delete'],
schema: @carto_user.database_schema
}
]
}
]
access_token = OauthAccessToken.create!(
oauth_app_user: @app_user,
scopes: ["datasets:rw:#{@materialized_view_name}"]
)
expect(access_token.api_key).to(be)
expect(access_token.api_key.type).to(eq('oauth'))
expect(access_token.api_key.grants).to(eq(expected_grants))
end
end
end
end