From c1b2c126dd3f766dc8271c1d349f506cf0734822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Miedes=20Garc=C3=A9s?= Date: Wed, 28 Oct 2020 16:13:47 +0100 Subject: [PATCH] Create new OAuth scope to read/write all datasets --- Makefile | 5 + NEWS.md | 1 + .../carto/api/public/datasets_controller.rb | 43 +---- app/models/carto/oauth_app_user.rb | 1 + app/models/carto/user_db_service.rb | 40 +++++ .../scopes/all_datasets_scope.rb | 82 +++++++++ lib/carto/oauth_provider/scopes/scopes.rb | 14 +- .../scopes/all_datasets_scope_spec.rb | 156 ++++++++++++++++++ .../oauth_provider/scopes/apis_scope_spec.rb | 16 ++ .../scopes/dataservices_scope_spec.rb | 23 +++ .../scopes/datasets_scope_spec.rb | 119 +++++++++++++ .../oauth_provider/scopes/scopes_spec.rb | 154 ----------------- .../oauth_provider/scopes/user_scope_spec.rb | 16 ++ 13 files changed, 479 insertions(+), 191 deletions(-) create mode 100644 lib/carto/oauth_provider/scopes/all_datasets_scope.rb create mode 100644 spec/lib/carto/oauth_provider/scopes/all_datasets_scope_spec.rb create mode 100644 spec/lib/carto/oauth_provider/scopes/apis_scope_spec.rb create mode 100644 spec/lib/carto/oauth_provider/scopes/dataservices_scope_spec.rb create mode 100644 spec/lib/carto/oauth_provider/scopes/datasets_scope_spec.rb create mode 100644 spec/lib/carto/oauth_provider/scopes/user_scope_spec.rb diff --git a/Makefile b/Makefile index d7864bc8be..a14b805365 100644 --- a/Makefile +++ b/Makefile @@ -329,7 +329,12 @@ SPEC_HELPER_MIN_SPECS = \ spec/lib/carto/styles/presenters/cartocss_spec.rb \ spec/lib/carto/forms_definition_spec.rb \ spec/lib/carto/form_spec.rb \ + spec/lib/carto/oauth_provider/scopes/all_datasets_scope_spec.rb \ + spec/lib/carto/oauth_provider/scopes/apis_scope_spec.rb \ + spec/lib/carto/oauth_provider/scopes/dataservices_scope_spec.rb \ + spec/lib/carto/oauth_provider/scopes/datasets_scope_spec.rb \ spec/lib/carto/oauth_provider/scopes/scopes_spec.rb \ + spec/lib/carto/oauth_provider/scopes/user_scope_spec.rb \ spec/models/carto/legend_spec.rb \ spec/requests/carto/api/legends_controller_spec.rb \ spec/lib/carto/legend_definition_validator_spec.rb \ diff --git a/NEWS.md b/NEWS.md index 1a075a8dae..e85f17900c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -18,6 +18,7 @@ Development * Adapt default Rails mail logs to JSON format [#15894](https://github.com/CartoDB/cartodb/pull/15894) * Fix export of Google Sheet files larger than 10MB [#15903](https://github.com/CartoDB/cartodb/pull/15903) * Adding `builder_url` to `api/v4/me` endpoint [#15904](https://github.com/CartoDB/cartodb/pull/15904) +* Create OAuth scope for reading/writing all datasets [#15884](https://github.com/CartoDB/cartodb/pull/15884) 4.42.0 (2020-09-28) ------------------- diff --git a/app/controllers/carto/api/public/datasets_controller.rb b/app/controllers/carto/api/public/datasets_controller.rb index 339b39810c..fec3cad3bd 100644 --- a/app/controllers/carto/api/public/datasets_controller.rb +++ b/app/controllers/carto/api/public/datasets_controller.rb @@ -21,9 +21,11 @@ module Carto VALID_ORDER_PARAMS = %i(name).freeze def index - tables = @user.in_database[select_tables_query].all + db_service = @user.carto_user.db_service + + tables = db_service.tables_granted(@query_params) result = enrich_tables(tables) - total = @user.in_database[count_tables_query].first[:count] + total = db_service.tables_granted_count render_paged(result, total) end @@ -39,6 +41,7 @@ module Carto VALID_ORDER_PARAMS, default_order: 'name', default_order_direction: 'asc' ) @offset = (@page - 1) * @per_page + @query_params = { order: @order, direction: @direction, limit: @per_page, offset: @offset } end def check_permissions @@ -47,12 +50,12 @@ module Carto end def enrich_tables(tables) - table_names = tables.map { |table| table[:name] } + table_names = tables.map(&:name) visualizations = table_visualizations(table_names) tables.map do |table| - viz = visualizations.find { |visualization| visualization[:name] == table[:name] } + viz = visualizations.find { |visualization| visualization[:name] == table.name } extra_fields = viz || default_extra_fields - table.merge(extra_fields) + table.to_h.merge(extra_fields) end end @@ -82,36 +85,6 @@ module Carto end end - def select_tables_query - %{ - SELECT * FROM (#{query}) AS q - ORDER BY #{@order} #{@direction} - LIMIT #{@per_page} - OFFSET #{@offset} - }.squish - end - - def count_tables_query - %{ - SELECT COUNT(*) FROM (#{query}) AS q - }.squish - end - - def query - roles_in = @user.db_service.all_user_roles.join("','") - %{ - SELECT table_schema, table_name as name, - string_agg(CASE privilege_type WHEN 'SELECT' THEN 'r' ELSE 'w' END, - '' order by privilege_type) as mode - FROM information_schema.role_table_grants - WHERE grantee IN ('#{roles_in}') - AND table_schema not in ('cartodb', 'aggregation') - AND grantor!='postgres' - AND privilege_type in ('SELECT', 'UPDATE') - GROUP BY table_schema,  table_name - }.squish - end - def render_paged(result, total) enriched_response = paged_result( result: result, diff --git a/app/models/carto/oauth_app_user.rb b/app/models/carto/oauth_app_user.rb index ed9581d6dd..46d0e2f99e 100644 --- a/app/models/carto/oauth_app_user.rb +++ b/app/models/carto/oauth_app_user.rb @@ -1,5 +1,6 @@ require_dependency 'carto/oauth_provider/scopes/scopes' require_dependency 'carto/oauth_provider/errors' +require_dependency 'carto/oauth_provider/scopes/scopes_validator' module Carto class OauthAppUser < ActiveRecord::Base diff --git a/app/models/carto/user_db_service.rb b/app/models/carto/user_db_service.rb index 2b643eb7db..60869a79d4 100644 --- a/app/models/carto/user_db_service.rb +++ b/app/models/carto/user_db_service.rb @@ -144,6 +144,46 @@ module Carto results.map { |row| [row['object_name'], row['granted_permissions'].split(',')] }.to_h end + # Returns all tables the user has access to, including shared ones and excluding internal tables. + # The result has this format: + # [] + def tables_granted(params = {}) + table = table_grants + table.project('*') + table.order("#{params[:order]} #{params[:direction]}") if params[:order] + table.take(params[:limit]) if params[:limit] + table.skip(params[:offset]) if params[:offset] + + @user.in_database.execute(table.to_sql).map { |t| OpenStruct.new(t) } + end + + def tables_granted_count + @user.in_database + .execute(table_grants.project('COUNT(*)').to_sql) + .first['count'].to_i + end + + def table_grants + table = Arel::Table.new('information_schema.role_table_grants') + query_sql = table.project(Arel.sql(%{ + table_schema, + table_name AS name, + STRING_AGG( + CASE privilege_type + WHEN 'SELECT' THEN 'r' + ELSE 'w' + END, + '' ORDER BY privilege_type + ) AS mode + })).where(Arel.sql(%{ + grantee IN ('#{all_user_roles.join("','")}') AND + table_schema NOT IN ('cartodb', 'aggregation') AND + grantor != 'postgres' AND + privilege_type IN ('SELECT', 'UPDATE') + })).group(Arel.sql('table_schema, table_name')).to_sql + Arel::SelectManager.new(Arel::Table.engine, Arel.sql("(#{query_sql}) AS q")) + end + def exists_role?(rolname) query = %{ SELECT 1 diff --git a/lib/carto/oauth_provider/scopes/all_datasets_scope.rb b/lib/carto/oauth_provider/scopes/all_datasets_scope.rb new file mode 100644 index 0000000000..c391cc39ef --- /dev/null +++ b/lib/carto/oauth_provider/scopes/all_datasets_scope.rb @@ -0,0 +1,82 @@ +module Carto + module OauthProvider + module Scopes + class AllDatasetsScope < DefaultScope + + READ_PERMISSIONS = ['select'].freeze + WRITE_PERMISSIONS = ['insert', 'update', 'delete'].freeze + + PERMISSIONS = { + r: READ_PERMISSIONS, + rw: READ_PERMISSIONS + WRITE_PERMISSIONS + }.with_indifferent_access.freeze + + DESCRIPTIONS = { + r: 'All datasets (read access)', + rw: 'All datasets (read/write access)' + }.with_indifferent_access.freeze + + EXCLUDED_INTERNAL_TABLES = %w( + geography_columns + geometry_columns + raster_columns + raster_overviews + spatial_ref_sys + ).freeze + + attr_reader :description, :permission_key, :permission + + def self.is_a?(scope) + scope =~ /^datasets:(?:rw|r):\*$/ + end + + def self.valid_scopes(scopes) + scopes.select { |scope| AllDatasetsScope.is_a?(scope) } + end + + def initialize(scope) + @permission_key = scope.split(':').second.to_sym + @permission = PERMISSIONS[permission_key] + @description = DESCRIPTIONS[permission_key] + @grant_key = :tables + super('database', permission_key, CATEGORY_DATASETS, description) + end + + def name + "datasets:#{permission_key}:*" + end + + def add_to_api_key_grants(grants, user) + ensure_includes_apis(grants, ['maps', 'sql']) + database_section = grant_section(grants) + granted_tables = user.db_service.tables_granted + + return unless granted_tables + + granted_tables.each do |table| + database_section[@grant_key] << { + name: table.name, + permissions: combined_permissions(table.mode.to_sym), + schema: table.table_schema + } + end + + ensure_grant_section(grants, database_section) + end + + private + + def combined_permissions(table_mode) + if permission_key == table_mode + PERMISSIONS[@permission_key] + elsif [permission_key, table_mode] == [:rw, :r] || [permission_key, table_mode] == [:r, :rw] + PERMISSIONS[:r] + else + [] + end + end + + end + end + end +end diff --git a/lib/carto/oauth_provider/scopes/scopes.rb b/lib/carto/oauth_provider/scopes/scopes.rb index 8a706b3275..83d479a20f 100644 --- a/lib/carto/oauth_provider/scopes/scopes.rb +++ b/lib/carto/oauth_provider/scopes/scopes.rb @@ -44,11 +44,19 @@ module Carto SUPPORTED_SCOPES = (SCOPES.map(&:name) - [SCOPE_DEFAULT]).freeze def self.invalid_scopes(scopes) - scopes - SUPPORTED_SCOPES - DatasetsScope.valid_scopes(scopes) - SchemasScope.valid_scopes(scopes) + scopes - + SUPPORTED_SCOPES - + DatasetsScope.valid_scopes(scopes) - + SchemasScope.valid_scopes(scopes) - + AllDatasetsScope.valid_scopes(scopes) end def self.invalid_scopes_and_tables(scopes, user) - scopes - SUPPORTED_SCOPES - DatasetsScope.valid_scopes_with_table(scopes, user) - SchemasScope.valid_scopes_with_schema(scopes, user) + scopes - + SUPPORTED_SCOPES - + DatasetsScope.valid_scopes_with_table(scopes, user) - + SchemasScope.valid_scopes_with_schema(scopes, user) - + AllDatasetsScope.valid_scopes(scopes) end def self.build(scope) @@ -56,6 +64,8 @@ module Carto if !result if DatasetsScope.is_a?(scope) result = DatasetsScope.new(scope) + elsif AllDatasetsScope.is_a?(scope) + result = AllDatasetsScope.new(scope) elsif SchemasScope.is_a?(scope) result = SchemasScope.new(scope) end diff --git a/spec/lib/carto/oauth_provider/scopes/all_datasets_scope_spec.rb b/spec/lib/carto/oauth_provider/scopes/all_datasets_scope_spec.rb new file mode 100644 index 0000000000..7866b81d82 --- /dev/null +++ b/spec/lib/carto/oauth_provider/scopes/all_datasets_scope_spec.rb @@ -0,0 +1,156 @@ +require 'spec_helper_min' +require './spec/support/factories/organizations' +require_dependency 'carto/oauth_provider/scopes/scopes' + +describe Carto::OauthProvider::Scopes::AllDatasetsScope do + include CartoDB::Factories + include Carto::Factories::Visualizations + include TableSharing + + let(:r_scope_string) { 'datasets:r:*' } + let(:rw_scope_string) { 'datasets:rw:*' } + let(:invalid_scope_string) { 'datasets:rx:*' } + let(:scope_string) { r_scope_string } + let(:scope) { described_class.new(scope_string) } + let(:user) { create(:valid_user).carto_user } + let!(:organization) { OrganizationFactory.new.create_organization_with_users.carto_organization } + let!(:org_admin) { organization.owner } + let(:organization_user) { organization.users.where.not(id: org_admin.id).first } + let(:tables_grants) { grants.find { |grant| grant[:type] == 'database' }[:tables] } + + describe '::is_a?' do + subject { described_class.is_a?(scope_string) } + + context 'when scope matches' do + it { should be_true } + end + + context 'when scope does not match' do + let(:scope_string) { invalid_scope_string } + + it { should be_false } + end + end + + describe '::valid_scopes' do + subject(:valid_scopes) { described_class.valid_scopes([r_scope_string, rw_scope_string, invalid_scope_string]) } + + it 'returns only the valid scopes' do + expect(valid_scopes).to include(r_scope_string) + expect(valid_scopes).to include(rw_scope_string) + expect(valid_scopes).not_to include(invalid_scope_string) + end + end + + describe '#name' do + subject(:name) { scope.name } + + context 'for read permissions' do + it 'returns the scope name' do + expect(name).to eq('datasets:r:*') + end + end + + context 'for read-write permissions' do + let(:scope_string) { rw_scope_string } + + it 'returns the scope name' do + expect(name).to eq('datasets:rw:*') + end + end + end + + describe '#add_to_api_key_grants' do + let!(:user_table) { create_full_visualization(user)[2] } + let(:grants) { [{ type: 'apis', apis: [] }] } + let(:granted_permissions) { tables_grants.find { |t| t[:name] == user_table.name }[:permissions] } + let(:granted_tables) { tables_grants.map { |table| table[:name] } } + + context 'when granting read-only permissions' do + before { scope.add_to_api_key_grants(grants, user) } + + it 'only grants read permissions' do + expect(granted_permissions).to include('select') + expect(granted_permissions).not_to include('insert') + expect(granted_permissions).not_to include('update') + expect(granted_permissions).not_to include('delete') + end + end + + context 'when granting read-write permissions' do + let(:scope_string) { rw_scope_string } + + before { scope.add_to_api_key_grants(grants, user) } + + it 'grants read-write permissions' do + expect(granted_permissions).to include('select') + expect(granted_permissions).to include('insert') + expect(granted_permissions).to include('update') + expect(granted_permissions).to include('delete') + end + end + + context 'when user belongs to organization' do + let(:user) { organization_user } + let!(:other_user_table) { create_full_visualization(org_admin)[2] } + + before do + user.update!(organization: organization) + scope.add_to_api_key_grants(grants, user) + end + + it 'only grants permissions to user datasets when in an organization' do + expect(granted_tables).to include(user_table.name) + expect(granted_tables).not_to include(other_user_table.name) + end + end + + context 'when user has access to other shared datasets' do + let(:user) { organization_user } + let(:full_visualization_objects) { create_full_visualization(org_admin) } + let(:other_table) { full_visualization_objects[1] } + let!(:other_user_table) { full_visualization_objects[2] } + let(:granted_permissions) { tables_grants.find { |t| t[:name] == other_user_table.name }[:permissions] } + + context 'with read-only access' do + before do + share_table_with_user(other_table, user) + scope.add_to_api_key_grants(grants, user) + end + + it 'only grants read permissions' do + expect(granted_tables).to include(other_user_table.name) + + expect(granted_permissions).to include('select') + expect(granted_permissions).not_to include('insert') + expect(granted_permissions).not_to include('update') + expect(granted_permissions).not_to include('delete') + end + end + + context 'with read-write access' do + let(:scope_string) { rw_scope_string } + + before do + share_table_with_user(other_table, user, access: Carto::Permission::ACCESS_READWRITE) + scope.add_to_api_key_grants(grants, user) + end + + it 'grants read-write permissions' do + expect(granted_tables).to include(other_user_table.name) + + expect(granted_permissions).to include('select') + expect(granted_permissions).to include('insert') + expect(granted_permissions).to include('update') + expect(granted_permissions).to include('delete') + end + end + end + + it 'does not grant permissions over internal tables' do + scope.add_to_api_key_grants(grants, user) + + expect(granted_tables).not_to include('spatial_ref_sys') + end + end +end diff --git a/spec/lib/carto/oauth_provider/scopes/apis_scope_spec.rb b/spec/lib/carto/oauth_provider/scopes/apis_scope_spec.rb new file mode 100644 index 0000000000..c8e0cda8f4 --- /dev/null +++ b/spec/lib/carto/oauth_provider/scopes/apis_scope_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper_min' +require_dependency 'carto/oauth_provider/scopes/scopes' +require_relative '../../../../factories/organizations_contexts' + +describe Carto::OauthProvider::Scopes::ApisScope do + include_context 'organization with users helper' + + describe '#add_to_api_key_grants' do + it 'adds apis scope with do subset' do + scope = Carto::OauthProvider::Scopes::ApisScope.new('do', 'Data Observatory API') + grants = [{ type: 'apis', apis: [] }] + scope.add_to_api_key_grants(grants, nil) + expect(grants).to(eq([{ type: 'apis', apis: ['do'] }])) + end + end +end diff --git a/spec/lib/carto/oauth_provider/scopes/dataservices_scope_spec.rb b/spec/lib/carto/oauth_provider/scopes/dataservices_scope_spec.rb new file mode 100644 index 0000000000..26d79932f9 --- /dev/null +++ b/spec/lib/carto/oauth_provider/scopes/dataservices_scope_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper_min' +require_dependency 'carto/oauth_provider/scopes/scopes' +require_relative '../../../../factories/organizations_contexts' + +describe Carto::OauthProvider::Scopes::DataservicesScope do + include_context 'organization with users helper' + + describe '#add_to_api_key_grants' do + let(:scope) { Carto::OauthProvider::Scopes::DataservicesScope.new('geocoding', 'GC') } + + it 'adds SQL api and dataservice' do + grants = [{ type: 'apis', apis: [] }] + scope.add_to_api_key_grants(grants, nil) + expect(grants).to(eq([{ type: 'apis', apis: ['sql'] }, { type: 'dataservices', services: ['geocoding'] }])) + end + + it 'does not add duplicate SQL api' do + grants = [{ type: 'apis', apis: ['sql'] }] + scope.add_to_api_key_grants(grants, nil) + expect(grants).to(include(type: 'apis', apis: ['sql'])) + end + end +end diff --git a/spec/lib/carto/oauth_provider/scopes/datasets_scope_spec.rb b/spec/lib/carto/oauth_provider/scopes/datasets_scope_spec.rb new file mode 100644 index 0000000000..90117657d3 --- /dev/null +++ b/spec/lib/carto/oauth_provider/scopes/datasets_scope_spec.rb @@ -0,0 +1,119 @@ +require 'spec_helper_min' +require_dependency 'carto/oauth_provider/scopes/scopes' +require_relative '../../../../factories/organizations_contexts' + +describe Carto::OauthProvider::Scopes::DatasetsScope do + include_context 'organization with users helper' + + describe '#add_to_api_key_grants' do + let(:full_dataset_scope) { Carto::OauthProvider::Scopes::DatasetsScope.new('datasets:rw:untitled_table') } + let(:read_dataset_scope) { Carto::OauthProvider::Scopes::DatasetsScope.new('datasets:r:untitled_table') } + let(:schema_scope) { Carto::OauthProvider::Scopes::SchemasScope.new('schemas:c') } + let(:dataset_metadata_scope) { Carto::OauthProvider::Scopes::DatasetsMetadataScope.new('datasets:metadata') } + let(:full_table_grants) do + [ + { + apis: [ + 'maps', + 'sql' + ], + type: 'apis' + }, + { + tables: [ + { + name: 'untitled_table', + permissions: [ + 'select', + 'insert', + 'update', + 'delete' + ], + schema: 'wadus' + } + ], + schemas: [ + { + name: 'wadus', + permissions: [ + 'create' + ] + } + ], + type: 'database' + } + ] + end + let(:read_table_grants) do + [ + { + apis: [ + 'maps', + 'sql' + ], + type: 'apis' + }, + { + tables: [ + { + name: 'untitled_table', + permissions: [ + 'select' + ], + schema: 'wadus' + } + ], + type: 'database' + } + ] + end + + let(:metadata_grants) do + [ + { + apis: [ + 'sql' + ], + type: 'apis' + }, + { + table_metadata: [], + type: 'database' + } + ] + end + + + before(:all) do + @user = mock + @user.stubs(:database_schema).returns('wadus') + end + + it 'adds full access permissions' do + grants = [{ type: 'apis', apis: [] }] + full_dataset_scope.add_to_api_key_grants(grants, @user) + schema_scope.add_to_api_key_grants(grants, @user) + expect(grants).to(eq(full_table_grants)) + end + + it 'does not add write permissions' do + grants = [{ type: 'apis', apis: [] }] + read_dataset_scope.add_to_api_key_grants(grants, @user) + expect(grants).to(eq(read_table_grants)) + end + + it 'adds metadata permissions' do + grants = [{ type: 'apis', apis: [] }] + dataset_metadata_scope.add_to_api_key_grants(grants, @user) + expect(grants).to(eq(metadata_grants)) + end + + it 'does add full access permissions and metadata' do + grants = [{ type: 'apis', apis: [] }] + dataset_metadata_scope.add_to_api_key_grants(grants, @user) + full_dataset_scope.add_to_api_key_grants(grants, @user) + expect(grants[1]).to have_key(:table_metadata) + expect(grants[1]).to have_key(:tables) + end + end +end diff --git a/spec/lib/carto/oauth_provider/scopes/scopes_spec.rb b/spec/lib/carto/oauth_provider/scopes/scopes_spec.rb index f10a9c64ef..df9bab98f5 100644 --- a/spec/lib/carto/oauth_provider/scopes/scopes_spec.rb +++ b/spec/lib/carto/oauth_provider/scopes/scopes_spec.rb @@ -301,160 +301,6 @@ describe Carto::OauthProvider::Scopes do end end - describe Carto::OauthProvider::Scopes::DataservicesScope do - describe '#add_to_api_key_grants' do - let(:scope) { Carto::OauthProvider::Scopes::DataservicesScope.new('geocoding', 'GC') } - - it 'adds SQL api and dataservice' do - grants = [{ type: 'apis', apis: [] }] - scope.add_to_api_key_grants(grants, nil) - expect(grants).to(eq([{ type: 'apis', apis: ['sql'] }, { type: 'dataservices', services: ['geocoding'] }])) - end - - it 'does not add duplicate SQL api' do - grants = [{ type: 'apis', apis: ['sql'] }] - scope.add_to_api_key_grants(grants, nil) - expect(grants).to(include(type: 'apis', apis: ['sql'])) - end - end - end - - describe Carto::OauthProvider::Scopes::UserScope do - describe '#add_to_api_key_grants' do - it 'adds user scope with profile subset' do - scope = Carto::OauthProvider::Scopes::UserScope.new('profile', 'User public profile') - grants = [{ type: 'apis', apis: [] }] - scope.add_to_api_key_grants(grants, nil) - expect(grants).to(eq([{ type: 'apis', apis: [] }, { type: 'user', data: ['profile'] }])) - end - end - end - - describe Carto::OauthProvider::Scopes::ApisScope do - describe '#add_to_api_key_grants' do - it 'adds apis scope with do subset' do - scope = Carto::OauthProvider::Scopes::ApisScope.new('do', 'Data Observatory API') - grants = [{ type: 'apis', apis: [] }] - scope.add_to_api_key_grants(grants, nil) - expect(grants).to(eq([{ type: 'apis', apis: ['do'] }])) - end - end - end - - describe Carto::OauthProvider::Scopes::DatasetsScope do - describe '#add_to_api_key_grants' do - let(:full_dataset_scope) { Carto::OauthProvider::Scopes::DatasetsScope.new('datasets:rw:untitled_table') } - let(:read_dataset_scope) { Carto::OauthProvider::Scopes::DatasetsScope.new('datasets:r:untitled_table') } - let(:schema_scope) { Carto::OauthProvider::Scopes::SchemasScope.new('schemas:c') } - let(:dataset_metadata_scope) { Carto::OauthProvider::Scopes::DatasetsMetadataScope.new('datasets:metadata') } - let(:full_table_grants) do - [ - { - apis: [ - 'maps', - 'sql' - ], - type: 'apis' - }, - { - tables: [ - { - name: 'untitled_table', - permissions: [ - 'select', - 'insert', - 'update', - 'delete' - ], - schema: 'wadus' - } - ], - schemas: [ - { - name: 'wadus', - permissions: [ - 'create' - ] - } - ], - type: 'database' - } - ] - end - let(:read_table_grants) do - [ - { - apis: [ - 'maps', - 'sql' - ], - type: 'apis' - }, - { - tables: [ - { - name: 'untitled_table', - permissions: [ - 'select' - ], - schema: 'wadus' - } - ], - type: 'database' - } - ] - end - - let(:metadata_grants) do - [ - { - apis: [ - 'sql' - ], - type: 'apis' - }, - { - table_metadata: [], - type: 'database' - } - ] - end - - - before(:all) do - @user = mock - @user.stubs(:database_schema).returns('wadus') - end - - it 'adds full access permissions' do - grants = [{ type: 'apis', apis: [] }] - full_dataset_scope.add_to_api_key_grants(grants, @user) - schema_scope.add_to_api_key_grants(grants, @user) - expect(grants).to(eq(full_table_grants)) - end - - it 'does not add write permissions' do - grants = [{ type: 'apis', apis: [] }] - read_dataset_scope.add_to_api_key_grants(grants, @user) - expect(grants).to(eq(read_table_grants)) - end - - it 'adds metadata permissions' do - grants = [{ type: 'apis', apis: [] }] - dataset_metadata_scope.add_to_api_key_grants(grants, @user) - expect(grants).to(eq(metadata_grants)) - end - - it 'does add full access permissions and metadata' do - grants = [{ type: 'apis', apis: [] }] - dataset_metadata_scope.add_to_api_key_grants(grants, @user) - full_dataset_scope.add_to_api_key_grants(grants, @user) - expect(grants[1]).to have_key(:table_metadata) - expect(grants[1]).to have_key(:tables) - end - end - end - describe '#subtract_scopes' do it 'r - r' do scopes1 = ['datasets:r:schema.table'] diff --git a/spec/lib/carto/oauth_provider/scopes/user_scope_spec.rb b/spec/lib/carto/oauth_provider/scopes/user_scope_spec.rb new file mode 100644 index 0000000000..46df39058b --- /dev/null +++ b/spec/lib/carto/oauth_provider/scopes/user_scope_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper_min' +require_dependency 'carto/oauth_provider/scopes/scopes' +require_relative '../../../../factories/organizations_contexts' + +describe Carto::OauthProvider::Scopes::UserScope do + include_context 'organization with users helper' + + describe '#add_to_api_key_grants' do + it 'adds user scope with profile subset' do + scope = Carto::OauthProvider::Scopes::UserScope.new('profile', 'User public profile') + grants = [{ type: 'apis', apis: [] }] + scope.add_to_api_key_grants(grants, nil) + expect(grants).to(eq([{ type: 'apis', apis: [] }, { type: 'user', data: ['profile'] }])) + end + end +end