diff --git a/Gemfile b/Gemfile index 6b1c43f40d..0d26949b6d 100644 --- a/Gemfile +++ b/Gemfile @@ -64,7 +64,7 @@ gem 'gibbon', '1.1.4' gem 'instagram-continued-continued' gem 'google-cloud-pubsub', '1.2.0' gem 'virtus', '1.0.5' -gem 'cartodb-common', git: 'https://github.com/cartodb/cartodb-common.git', tag: 'v0.3.4' +gem 'cartodb-common', git: 'https://github.com/cartodb/cartodb-common.git', tag: 'v0.3.6' gem 'email_address', '~> 0.1.11' gem 'redcarpet', '3.3.3' gem 'rollbar', '~>2.11.1' diff --git a/Gemfile.lock b/Gemfile.lock index 826edd37af..c715ebf5ea 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,10 +8,10 @@ GIT GIT remote: https://github.com/cartodb/cartodb-common.git - revision: 341004053e7198ab1021fb0722de18acd4a09520 - tag: v0.3.4 + revision: d6f603d4324587de1ae0b60af14856e2935af75c + tag: v0.3.6 specs: - cartodb-common (0.3.0) + cartodb-common (0.3.6) activesupport (~> 4.2.11.3) argon2 (~> 2.0) diff --git a/NEWS.md b/NEWS.md index 010ee9730f..465528fe59 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,7 +5,7 @@ Development - None yet ### Features -- None yet +* New management capabilities for API Keys of other users ([#15819](https://github.com/CartoDB/cartodb/pull/15819)) ### Bug fixes / enhancements - Return expired subscriptions with status 'expired' ([93673](https://app.clubhouse.io/cartoteam/story/93673/return-expired-subscriptions)) @@ -22,7 +22,10 @@ Development * Adds logging docs [#15813](https://github.com/CartoDB/cartodb/pull/15813) * Add wildcard IP for Direct SQL connection [#15818](https://github.com/CartoDB/cartodb/pull/15818) * Remove usage of `::User` Sequel model from the `ApplicationController` [#15804](https://github.com/CartoDB/cartodb/pull/15804) +* Bump version of cartodb-common module to v0.3.6 [#15820](https://github.com/CartoDB/cartodb/pull/15820) * Setup Coverband dead code detector [https://github.com/CartoDB/cartodb/pull/15811](https://github.com/CartoDB/cartodb/pull/15811) +* Include LoggerHelper as class methods in models [https://github.com/CartoDB/cartodb/pull/15824](https://github.com/CartoDB/cartodb/pull/15824) +* Fix Coverband - Resque integration [#15827](https://github.com/CartoDB/cartodb/pull/15827) 4.41.1 (2020-09-03) ------------------- diff --git a/app/controllers/carto/api/api_keys_controller.rb b/app/controllers/carto/api/api_keys_controller.rb index cc83e43038..915799caa1 100644 --- a/app/controllers/carto/api/api_keys_controller.rb +++ b/app/controllers/carto/api/api_keys_controller.rb @@ -24,8 +24,7 @@ class Carto::Api::ApiKeysController < ::Api::ApplicationController Carto::ApiKey::TYPE_REGULAR].freeze def create - carto_viewer = Carto::User.find(current_viewer.id) - api_key = carto_viewer.api_keys.create_regular_key!(name: params[:name], grants: params[:grants]) + api_key = target_user.api_keys.create_regular_key!(name: params[:name], grants: params[:grants]) render_jsonp(Carto::Api::ApiKeyPresenter.new(api_key).to_poro, 201) rescue ActiveRecord::RecordInvalid => e raise Carto::UnprocesableEntityError.new(e.message) @@ -48,7 +47,7 @@ class Carto::Api::ApiKeysController < ::Api::ApplicationController def index page, per_page, order, _order_direction = page_per_page_order_params(VALID_ORDER_PARAMS) - api_keys = Carto::User.find(current_user.id).api_keys.by_type(type_param).order_weighted_by_type + api_keys = target_user.api_keys.by_type(type_param).order_weighted_by_type api_keys = request_api_key.master? ? api_keys : api_keys.where(id: request_api_key.id) filtered_api_keys = Carto::PagedModel.paged_association(api_keys, page, per_page, order) @@ -74,7 +73,7 @@ class Carto::Api::ApiKeysController < ::Api::ApplicationController def load_api_key name = params[:id] - @viewed_api_key = Carto::ApiKey.where(user_id: current_viewer.id, name: name).user_visible.first + @viewed_api_key = Carto::ApiKey.where(user_id: target_user.id, name: name).user_visible.first if !@viewed_api_key || !request_api_key.master? && @viewed_api_key != request_api_key raise Carto::LoadError.new("API key not found: #{name}") end @@ -93,4 +92,16 @@ class Carto::Api::ApiKeysController < ::Api::ApplicationController raise Carto::ParamInvalidError.new(:type, VALID_TYPE_PARAMS) unless (types - VALID_TYPE_PARAMS).empty? types end + + def target_user + if params[:target_user].nil? + current_viewer + else + # just org owners or org admins can manage api keys for other users + raise Carto::UnauthorizedError.new unless current_viewer.organization_admin? + user = Carto::User.where(username: params[:target_user], organization: current_viewer.organization.id).first + raise Carto::LoadError.new("User '#{params[:target_user]}' not found in the organization '#{current_viewer.organization.name}'") if user.nil? + user + end + end end diff --git a/config/coverband.rb b/config/coverband.rb index d8c7895c41..f22189b69e 100644 --- a/config/coverband.rb +++ b/config/coverband.rb @@ -1,3 +1,6 @@ +# Load the ::Resque constant manually so Coverband installs the corresponding hooks +# https://github.com/danmayer/coverband/blob/v5.0.1/lib/coverband.rb#L114 +require 'resque' require 'coverband' Coverband.configure do |config| diff --git a/config/initializers/04_install_carto_logger.rb b/config/initializers/04_install_carto_logger.rb index b949472e5f..eb9e762695 100644 --- a/config/initializers/04_install_carto_logger.rb +++ b/config/initializers/04_install_carto_logger.rb @@ -1,5 +1,12 @@ Carto::Common::Logger.install # Log more easily from all models -ActiveRecord::Base.class_eval { include ::LoggerHelper } -Sequel::Model.class_eval { include ::LoggerHelper } +ActiveRecord::Base.class_eval do + include ::LoggerHelper + extend ::LoggerHelper +end + +Sequel::Model.class_eval do + include ::LoggerHelper + extend ::LoggerHelper +end diff --git a/doc/developer-center/auth-api/reference/swagger.yaml b/doc/developer-center/auth-api/reference/swagger.yaml index 44e9293003..d27fa16829 100644 --- a/doc/developer-center/auth-api/reference/swagger.yaml +++ b/doc/developer-center/auth-api/reference/swagger.yaml @@ -11,7 +11,7 @@ info: This API accepts and returns JSON. - API base endpoint is `https://.carto.com/api/v3/api_keys` or `https://.carto.com/u/.carto.com/api/v3/api_keys` or `https://.carto.com/u//api/v3/api_keys` in case you are using a user belonging to an organization. # Authorization @@ -140,6 +140,12 @@ paths: type: string description: Its used to define the critera by which API Keys are listed. It can be any of the attributes required: false + - in: query + name: target_user + schema: + type: string + description: Username of the user belonging to the organization whose API Keys will be listed + required: false responses: '200': description: Ok @@ -151,6 +157,10 @@ paths: $ref: '#/components/schemas/ApiKeys' '401': $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' security: - ApiKeyHTTPBasicAuth: [] - ApiKeyQueryParam: [] @@ -163,6 +173,13 @@ paths: tags: - API Keys operationId: createApiKey + parameters: + - in: query + name: target_user + schema: + type: string + description: Username of the user belonging to the organization for whom the new `regular` API key will be created + required: false requestBody: required: true content: @@ -178,6 +195,10 @@ paths: $ref: '#/components/schemas/ApiKey' '401': $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/BadInput' security: @@ -195,6 +216,13 @@ paths: tags: - API Keys operationId: getApiKeyById + parameters: + - in: query + name: target_user + schema: + type: string + description: Username of the user belonging to the organization who has the API key requested + required: false responses: '200': description: Ok @@ -204,6 +232,8 @@ paths: $ref: '#/components/schemas/ApiKey' '401': $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' security: @@ -218,6 +248,13 @@ paths: tags: - API Keys operationId: deleteApiKeyById + parameters: + - in: query + name: target_user + schema: + type: string + description: Username of the user belonging to the organization who has the API key that will be deleted + required: false responses: '200': description: The resource was deleted successfully. @@ -241,6 +278,13 @@ paths: tags: - API Keys operationId: regenerateApiKeyById + parameters: + - in: query + name: target_user + schema: + type: string + description: Username of the user belonging to the organization who has the API key whose token will be regenerated + required: false responses: '200': description: Ok @@ -250,6 +294,8 @@ paths: $ref: '#/components/schemas/ApiKey' '401': $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' security: diff --git a/spec/requests/carto/api/api_keys_controller_spec.rb b/spec/requests/carto/api/api_keys_controller_spec.rb index 15becd89ef..692a35098e 100644 --- a/spec/requests/carto/api/api_keys_controller_spec.rb +++ b/spec/requests/carto/api/api_keys_controller_spec.rb @@ -1117,4 +1117,730 @@ describe Carto::Api::ApiKeysController do end end end + + describe 'managing api keys for other organization users' do + def auth_user(u) + @auth_user = u + end + + def auth_headers + json_headers_with_auth(@auth_user.username, @auth_user.api_key) + end + + def auth_params + { user_domain: @auth_user.username } + end + + before :all do + @num_api_keys_owner_user = 4 + @num_api_keys_admin_user = 3 + @num_api_keys_regular_user = 2 + @num_api_keys_external_user = 1 + + # create org and owner + org = FactoryGirl.create(:organization_with_users) + @owner_user = org.owner + @carto_owner_user = Carto::User.find(@owner_user.id) + apikeys = @carto_owner_user.api_keys.order(:updated_at).all.to_a + @num_api_keys_owner_user.times { apikeys << FactoryGirl.create(:api_key_apis, user_id: @owner_user.id) } + apikeys << FactoryGirl.create(:oauth_api_key, user_id: @owner_user.id) + @owner_api_key = apikeys[3] + @owner_table1 = create_table(user_id: @carto_owner_user.id) + @owner_table2 = create_table(user_id: @carto_owner_user.id) + @owner_api_key_grants = [ + { + type: "apis", + apis: ["sql", "maps"] + }, + { + type: "database", + tables: [ + { + schema: @carto_owner_user.database_schema, + name: @owner_table1.name, + permissions: [ + "insert", + "select", + "update", + "delete" + ] + }, + { + schema: @carto_owner_user.database_schema, + name: @owner_table2.name, + permissions: [ + "select" + ] + } + ] + } + ] + @owner_api_key_name = 'owner_wadus' + @owner_api_key_payload = { + name: @owner_api_key_name, + grants: @owner_api_key_grants + } + + # create admin + @admin_user = FactoryGirl.create(:valid_user, organization: org, org_admin: true) + @carto_admin_user = Carto::User.find(@admin_user.id) + apikeys = @carto_admin_user.api_keys.order(:updated_at).all.to_a + @num_api_keys_admin_user.times { apikeys << FactoryGirl.create(:api_key_apis, user_id: @admin_user.id) } + apikeys << FactoryGirl.create(:oauth_api_key, user_id: @admin_user.id) + @admin_api_key = apikeys[3] + @admin_table1 = create_table(user_id: @carto_admin_user.id) + @admin_table2 = create_table(user_id: @carto_admin_user.id) + @admin_api_key_grants = [ + { + type: "apis", + apis: ["sql", "maps"] + }, + { + type: "database", + tables: [ + { + schema: @carto_admin_user.database_schema, + name: @admin_table1.name, + permissions: [ + "insert", + "select", + "update", + "delete" + ] + }, + { + schema: @carto_admin_user.database_schema, + name: @admin_table2.name, + permissions: [ + "select" + ] + } + ] + } + ] + @admin_api_key_name = 'admin_wadus' + @admin_api_key_payload = { + name: @admin_api_key_name, + grants: @admin_api_key_grants + } + + # create regular + @regular_user = FactoryGirl.create(:valid_user, organization: org) + @carto_regular_user = Carto::User.find(@regular_user.id) + apikeys = @carto_regular_user.api_keys.order(:updated_at).all.to_a + @num_api_keys_regular_user.times { apikeys << FactoryGirl.create(:api_key_apis, user_id: @regular_user.id) } + apikeys << FactoryGirl.create(:oauth_api_key, user_id: @regular_user.id) + @regular_api_key = apikeys[3] + @regular_table1 = create_table(user_id: @carto_regular_user.id) + @regular_table2 = create_table(user_id: @carto_regular_user.id) + @regular_api_key_grants = [ + { + type: "apis", + apis: ["sql", "maps"] + }, + { + type: "database", + tables: [ + { + schema: @carto_regular_user.database_schema, + name: @regular_table1.name, + permissions: [ + "insert", + "select", + "update", + "delete" + ] + }, + { + schema: @carto_regular_user.database_schema, + name: @regular_table2.name, + permissions: [ + "select" + ] + } + ] + } + ] + @regular_api_key_name = 'regular_wadus' + @regular_api_key_payload = { + name: @regular_api_key_name, + grants: @regular_api_key_grants + } + + # external user + @external_user = FactoryGirl.create(:valid_user) + @carto_external_user = Carto::User.find(@external_user.id) + apikeys = @carto_external_user.api_keys.order(:updated_at).all.to_a + @num_api_keys_external_user.times { apikeys << FactoryGirl.create(:api_key_apis, user_id: @external_user.id) } + apikeys << FactoryGirl.create(:oauth_api_key, user_id: @external_user.id) + @external_api_key = apikeys[3] + @external_table1 = create_table(user_id: @carto_external_user.id) + @external_table2 = create_table(user_id: @carto_external_user.id) + @external_api_key_grants = [ + { + type: "apis", + apis: ["sql", "maps"] + }, + { + type: "database", + tables: [ + { + schema: @carto_external_user.database_schema, + name: @external_table1.name, + permissions: [ + "insert", + "select", + "update", + "delete" + ] + }, + { + schema: @carto_external_user.database_schema, + name: @external_table2.name, + permissions: [ + "select" + ] + } + ] + } + ] + @external_api_key_name = 'external_wadus' + @external_api_key_payload = { + name: @external_api_key_name, + grants: @external_api_key_grants + } + + end + + after :all do + @owner_table1.destroy + @owner_table2.destroy + @admin_table1.destroy + @admin_table2.destroy + @regular_table1.destroy + @regular_table2.destroy + @external_table1.destroy + @external_table2.destroy + end + + describe '#index' do + describe 'owner org' do + it 'can list regular user api keys' do + auth_user(@carto_owner_user) + get_json api_keys_url, auth_params.merge(per_page: 20, target_user: @carto_regular_user.username), auth_headers do |response| + response.status.should eq 200 + response.body[:result][0][:type].should eq 'master' + response.body[:result][1][:type].should eq 'default' + response.body[:result][2][:type].should eq 'regular' + response.body[:result].length.should eq @num_api_keys_regular_user + 2 # master and default + end + end + + it 'can list admin user api keys' do + auth_user(@carto_owner_user) + get_json api_keys_url, auth_params.merge(per_page: 20, target_user: @carto_admin_user.username), auth_headers do |response| + response.status.should eq 200 + response.body[:result][0][:type].should eq 'master' + response.body[:result][1][:type].should eq 'default' + response.body[:result][2][:type].should eq 'regular' + response.body[:result].length.should eq @num_api_keys_admin_user + 2 # master and default + end + end + + it 'cannot list external user api keys' do + auth_user(@carto_owner_user) + get_json api_keys_url, auth_params.merge(per_page: 20, target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 404 + response.body[:errors].should match /not found in the organization/ + end + end + end + + describe 'admin org' do + it 'can list regular user api keys' do + auth_user(@carto_admin_user) + get_json api_keys_url, auth_params.merge(per_page: 20, target_user: @carto_regular_user.username), auth_headers do |response| + response.status.should eq 200 + response.body[:result][0][:type].should eq 'master' + response.body[:result][1][:type].should eq 'default' + response.body[:result][2][:type].should eq 'regular' + response.body[:result].length.should eq @num_api_keys_regular_user + 2 # master and default + end + end + + it 'can list owner user api keys' do + auth_user(@carto_admin_user) + get_json api_keys_url, auth_params.merge(per_page: 20, target_user: @carto_owner_user.username), auth_headers do |response| + response.status.should eq 200 + response.body[:result][0][:type].should eq 'master' + response.body[:result][1][:type].should eq 'default' + response.body[:result][2][:type].should eq 'regular' + response.body[:result].length.should eq @num_api_keys_owner_user + 2 # master and default + end + end + + it 'cannot list external user api keys' do + auth_user(@carto_admin_user) + get_json api_keys_url, auth_params.merge(per_page: 20, target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 404 + response.body[:errors].should match /not found in the organization/ + end + end + end + + describe 'regular user' do + it 'cannot list owner user api keys' do + auth_user(@carto_regular_user) + get_json api_keys_url, auth_params.merge(per_page: 20, target_user: @carto_owner_user.username), auth_headers do |response| + response.status.should eq 403 + response.body[:errors].should match /don't have permission to access/ + end + end + + it 'cannot list admin user api keys' do + auth_user(@carto_regular_user) + get_json api_keys_url, auth_params.merge(per_page: 20, target_user: @carto_admin_user.username), auth_headers do |response| + response.status.should eq 403 + response.body[:errors].should match /don't have permission to access/ + end + end + + it 'cannot list external user api keys' do + auth_user(@carto_regular_user) + get_json api_keys_url, auth_params.merge(per_page: 20, target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 403 + response.body[:errors].should match /don't have permission to access/ + end + end + end + end + + describe '#show' do + describe 'owner org' do + it 'can show info of a regular user api key' do + auth_user(@carto_owner_user) + get_json api_key_url(id: @regular_api_key.name), auth_params.merge(target_user: @carto_regular_user.username), auth_headers do |response| + response.status.should eq 200 + response.body[:name] = @regular_api_key.name + end + end + + it 'can show info of an admin user api key' do + auth_user(@carto_owner_user) + get_json api_key_url(id: @admin_api_key.name), auth_params.merge(target_user: @carto_admin_user.username), auth_headers do |response| + response.status.should eq 200 + response.body[:name] = @admin_api_key.name + end + end + + it 'cannot show info of an admin user api key without the target_user parameter' do + auth_user(@carto_owner_user) + get_json api_key_url(id: @admin_api_key.name), auth_params, auth_headers do |response| + response.status.should eq 404 + response.body[:errors].should match /API key not found/ + end + end + + it 'cannot show info of an external user api key' do + auth_user(@carto_owner_user) + get_json api_key_url(id: @external_api_key.name), auth_params.merge(target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 404 + response.body[:errors].should match /not found in the organization/ + end + end + end + + describe 'admin org' do + it 'can show info of a regular user api key' do + auth_user(@carto_admin_user) + get_json api_key_url(id: @regular_api_key.name), auth_params.merge(target_user: @carto_regular_user.username), auth_headers do |response| + response.status.should eq 200 + response.body[:name] = @regular_api_key.name + end + end + + it 'can show info of an owner user api key' do + auth_user(@carto_admin_user) + get_json api_key_url(id: @owner_api_key.name), auth_params.merge(target_user: @carto_owner_user.username), auth_headers do |response| + response.status.should eq 200 + response.body[:name] = @owner_api_key.name + end + end + + it 'cannot show info of an owner user api key without the target_user parameter' do + auth_user(@carto_admin_user) + get_json api_key_url(id: @owner_api_key.name), auth_params, auth_headers do |response| + response.status.should eq 404 + response.body[:errors].should match /API key not found/ + end + end + + it 'cannot show info of an external user api key' do + auth_user(@carto_admin_user) + get_json api_key_url(id: @external_api_key.name), auth_params.merge(target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 404 + response.body[:errors].should match /not found in the organization/ + end + end + end + + describe 'regular user' do + it 'cannot show info of an owner user api key' do + auth_user(@carto_regular_user) + get_json api_key_url(id: @external_api_key.name), auth_params.merge(target_user: @carto_owner_user.username), auth_headers do |response| + response.status.should eq 403 + response.body[:errors].should match /don't have permission to access/ + end + end + + it 'cannot show info of an admin user api key' do + auth_user(@carto_regular_user) + get_json api_key_url(id: @external_api_key.name), auth_params.merge(target_user: @carto_admin_user.username), auth_headers do |response| + response.status.should eq 403 + response.body[:errors].should match /don't have permission to access/ + end + end + + it 'cannot show info of an external user api key' do + auth_user(@carto_regular_user) + get_json api_key_url(id: @external_api_key.name), auth_params.merge(target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 403 + response.body[:errors].should match /don't have permission to access/ + end + end + end + end + + describe '#create' do + describe 'owner org' do + it 'can create a regular user api key' do + auth_user(@carto_owner_user) + post_json api_keys_url, auth_params.merge(@regular_api_key_payload).merge(target_user: @carto_regular_user.username), auth_headers do |response| + response.status.should eq 201 + api_key_response = response.body + api_key_response[:id].should_not be + api_key_response[:name].should eq @regular_api_key_name + api_key_response[:user][:username].should eq @carto_regular_user.username + api_key_response[:type].should eq 'regular' + api_key_response[:token].should_not be_empty + + request_table_permissions = @regular_api_key_grants.find { |grant| grant[:type] == 'database' }[:tables] + response_grants_should_include_request_permissions(api_key_response[:grants], request_table_permissions) + + api_key_response[:databaseConfig].should_not be + + Carto::ApiKey.where(name: api_key_response[:name]).each(&:destroy) + end + end + + it 'can create an admin user api key' do + auth_user(@carto_owner_user) + post_json api_keys_url, auth_params.merge(@admin_api_key_payload).merge(target_user: @carto_admin_user.username), auth_headers do |response| + response.status.should eq 201 + api_key_response = response.body + api_key_response[:id].should_not be + api_key_response[:name].should eq @admin_api_key_name + api_key_response[:user][:username].should eq @carto_admin_user.username + api_key_response[:type].should eq 'regular' + api_key_response[:token].should_not be_empty + + request_table_permissions = @admin_api_key_grants.find { |grant| grant[:type] == 'database' }[:tables] + response_grants_should_include_request_permissions(api_key_response[:grants], request_table_permissions) + + api_key_response[:databaseConfig].should_not be + + Carto::ApiKey.where(name: api_key_response[:name]).each(&:destroy) + end + end + + it 'cannot create an external user api key' do + auth_user(@carto_owner_user) + post_json api_keys_url, auth_params.merge(@external_api_key_payload).merge(target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 404 + response.body[:errors].should match /not found in the organization/ + end + end + end + + describe 'admin org' do + it 'can create a regular user api key' do + auth_user(@carto_admin_user) + post_json api_keys_url, auth_params.merge(@regular_api_key_payload).merge(target_user: @carto_regular_user.username), auth_headers do |response| + response.status.should eq 201 + api_key_response = response.body + api_key_response[:id].should_not be + api_key_response[:name].should eq @regular_api_key_name + api_key_response[:user][:username].should eq @carto_regular_user.username + api_key_response[:type].should eq 'regular' + api_key_response[:token].should_not be_empty + + request_table_permissions = @regular_api_key_grants.find { |grant| grant[:type] == 'database' }[:tables] + response_grants_should_include_request_permissions(api_key_response[:grants], request_table_permissions) + + api_key_response[:databaseConfig].should_not be + + Carto::ApiKey.where(name: api_key_response[:name]).each(&:destroy) + end + end + + it 'can create an owner user api key' do + auth_user(@carto_admin_user) + post_json api_keys_url, auth_params.merge(@owner_api_key_payload).merge(target_user: @carto_owner_user.username), auth_headers do |response| + response.status.should eq 201 + api_key_response = response.body + api_key_response[:id].should_not be + api_key_response[:name].should eq @owner_api_key_name + api_key_response[:user][:username].should eq @carto_owner_user.username + api_key_response[:type].should eq 'regular' + api_key_response[:token].should_not be_empty + + request_table_permissions = @owner_api_key_grants.find { |grant| grant[:type] == 'database' }[:tables] + response_grants_should_include_request_permissions(api_key_response[:grants], request_table_permissions) + + api_key_response[:databaseConfig].should_not be + + Carto::ApiKey.where(name: api_key_response[:name]).each(&:destroy) + end + end + + it 'cannot create an external user api key' do + auth_user(@carto_admin_user) + post_json api_keys_url, auth_params.merge(@external_api_key_payload).merge(target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 404 + response.body[:errors].should match /not found in the organization/ + end + end + end + + describe 'regular user' do + it 'cannot create an owner user api key' do + auth_user(@carto_regular_user) + post_json api_keys_url, auth_params.merge(@owner_api_key_payload).merge(target_user: @carto_owner_user.username), auth_headers do |response| + response.status.should eq 403 + response.body[:errors].should match /don't have permission to access/ + end + end + + it 'cannot create an admin user api key' do + auth_user(@carto_regular_user) + post_json api_keys_url, auth_params.merge(@admin_api_key_payload).merge(target_user: @carto_admin_user.username), auth_headers do |response| + response.status.should eq 403 + response.body[:errors].should match /don't have permission to access/ + end + end + + it 'cannot create an external user api key' do + auth_user(@carto_regular_user) + post_json api_keys_url, auth_params.merge(@external_api_key_payload).merge(target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 403 + response.body[:errors].should match /don't have permission to access/ + end + end + end + end + + describe '#destroy' do + describe 'owner org' do + it 'can destroy a regular user api key' do + api_key = FactoryGirl.create(:api_key_apis, user_id: @carto_regular_user.id) + auth_user(@carto_owner_user) + delete_json api_key_url(id: api_key.name), auth_params.merge(target_user: @carto_regular_user.username), auth_headers do |response| + response.status.should eq 204 + end + + Carto::ApiKey.where(name: api_key.name, user_id: @carto_regular_user.id).first.should be_nil + end + + it 'can destroy an admin user api key' do + api_key = FactoryGirl.create(:api_key_apis, user_id: @carto_admin_user.id) + auth_user(@carto_owner_user) + delete_json api_key_url(id: api_key.name), auth_params.merge(target_user: @carto_admin_user.username), auth_headers do |response| + response.status.should eq 204 + end + + Carto::ApiKey.where(name: api_key.name, user_id: @carto_admin_user.id).first.should be_nil + end + + it 'cannot destroy an external user api key' do + auth_user(@carto_owner_user) + delete_json api_key_url(id: 'foo'), auth_params.merge(target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 404 + response.body[:errors].should match /not found in the organization/ + end + end + end + + describe 'admin org' do + it 'can destroy a regular user api key' do + api_key = FactoryGirl.create(:api_key_apis, user_id: @carto_regular_user.id) + auth_user(@carto_admin_user) + delete_json api_key_url(id: api_key.name), auth_params.merge(target_user: @carto_regular_user.username), auth_headers do |response| + response.status.should eq 204 + end + + Carto::ApiKey.where(name: api_key.name, user_id: @carto_regular_user.id).first.should be_nil + end + + it 'can destroy an owner user api key' do + api_key = FactoryGirl.create(:api_key_apis, user_id: @carto_owner_user.id) + auth_user(@carto_admin_user) + delete_json api_key_url(id: api_key.name), auth_params.merge(target_user: @carto_owner_user.username), auth_headers do |response| + response.status.should eq 204 + end + + Carto::ApiKey.where(name: api_key.name, user_id: @carto_owner_user.id).first.should be_nil + end + + it 'cannot destroy an external user api key' do + auth_user(@carto_admin_user) + delete_json api_key_url(id: @external_api_key.name), auth_params.merge(target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 404 + response.body[:errors].should match /not found in the organization/ + end + end + end + + describe 'regular user' do + it 'cannot destroy an owner user api key' do + auth_user(@carto_regular_user) + delete_json api_key_url(id: @owner_api_key.name), auth_params.merge(target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 403 + response.body[:errors].should match /don't have permission to access/ + end + end + + it 'cannot destroy an admin user api key' do + auth_user(@carto_regular_user) + delete_json api_key_url(id: @admin_api_key.name), auth_params.merge(target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 403 + response.body[:errors].should match /don't have permission to access/ + end + end + + it 'cannot destroy an external user api key' do + auth_user(@carto_regular_user) + delete_json api_key_url(id: @external_api_key.name), auth_params.merge(target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 403 + response.body[:errors].should match /don't have permission to access/ + end + end + end + end + + describe '#regenerate_token' do + describe 'owner org' do + it 'can regenerate the token of a regular user api key' do + old_token = @regular_api_key.token + auth_user(@carto_owner_user) + post_json regenerate_api_key_token_url(id: @regular_api_key.name), auth_params.merge(target_user: @carto_regular_user.username), auth_headers do |response| + response.status.should eq 200 + response.body[:token].should_not be_nil + response.body[:token].should_not eq old_token + @regular_api_key.reload + response.body[:token].should eq @regular_api_key.token + end + end + + it 'can regenerate the token of an admin user api key' do + old_token = @admin_api_key.token + auth_user(@carto_owner_user) + post_json regenerate_api_key_token_url(id: @admin_api_key.name), auth_params.merge(target_user: @carto_admin_user.username), auth_headers do |response| + response.status.should eq 200 + response.body[:token].should_not be_nil + response.body[:token].should_not eq old_token + @admin_api_key.reload + response.body[:token].should eq @admin_api_key.token + end + end + + it 'cannot regenerate the token of an admin user api key without the target_user parameter' do + old_token = @admin_api_key.token + auth_user(@carto_owner_user) + post_json regenerate_api_key_token_url(id: @admin_api_key.name), auth_params, auth_headers do |response| + response.status.should eq 404 + response.body[:errors].should match /API key not found/ + end + end + + it 'cannot regenerate the token of an external user api key' do + old_token = @external_api_key.token + auth_user(@carto_owner_user) + post_json regenerate_api_key_token_url(id: @external_api_key.name), auth_params.merge(target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 404 + response.body[:errors].should match /not found in the organization/ + end + end + end + + describe 'admin org' do + it 'can regenerate the token of a regular user api key' do + old_token = @regular_api_key.token + auth_user(@carto_admin_user) + post_json regenerate_api_key_token_url(id: @regular_api_key.name), auth_params.merge(target_user: @carto_regular_user.username), auth_headers do |response| + response.status.should eq 200 + response.body[:token].should_not be_nil + response.body[:token].should_not eq old_token + @regular_api_key.reload + response.body[:token].should eq @regular_api_key.token + end + end + + it 'can regenerate the token of an owner user api key' do + old_token = @owner_api_key.token + auth_user(@carto_admin_user) + post_json regenerate_api_key_token_url(id: @owner_api_key.name), auth_params.merge(target_user: @carto_owner_user.username), auth_headers do |response| + response.status.should eq 200 + response.body[:token].should_not be_nil + response.body[:token].should_not eq old_token + @owner_api_key.reload + response.body[:token].should eq @owner_api_key.token + end + end + + it 'cannot regenerate the token of an owner user api key without the target_user parameter' do + old_token = @owner_api_key.token + auth_user(@carto_admin_user) + post_json regenerate_api_key_token_url(id: @owner_api_key.name), auth_params, auth_headers do |response| + response.status.should eq 404 + response.body[:errors].should match /API key not found/ + end + end + + it 'cannot regenerate the token of an external user api key' do + old_token = @external_api_key.token + auth_user(@carto_admin_user) + post_json regenerate_api_key_token_url(id: @external_api_key.name), auth_params.merge(target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 404 + response.body[:errors].should match /not found in the organization/ + end + end + end + + describe 'regular user' do + it 'cannot regenerate the token of an owner user api key' do + auth_user(@carto_regular_user) + post_json regenerate_api_key_token_url(id: @owner_api_key.name), auth_params.merge(target_user: @carto_owner_user.username), auth_headers do |response| + response.status.should eq 403 + response.body[:errors].should match /don't have permission to access/ + end + end + + it 'cannot regenerate the token of an admin user api key' do + auth_user(@carto_regular_user) + post_json regenerate_api_key_token_url(id: @admin_api_key.name), auth_params.merge(target_user: @carto_admin_user.username), auth_headers do |response| + response.status.should eq 403 + response.body[:errors].should match /don't have permission to access/ + end + end + + it 'cannot regenerate the token of an external user api key' do + auth_user(@carto_regular_user) + post_json regenerate_api_key_token_url(id: @external_api_key.name), auth_params.merge(target_user: @carto_external_user.username), auth_headers do |response| + response.status.should eq 403 + response.body[:errors].should match /don't have permission to access/ + end + end + end + end + end end