diff --git a/lib/travis/api/serialize/v2/http.rb b/lib/travis/api/serialize/v2/http.rb index e697207ea..4b8ca4974 100644 --- a/lib/travis/api/serialize/v2/http.rb +++ b/lib/travis/api/serialize/v2/http.rb @@ -21,3 +21,4 @@ require 'travis/api/serialize/v2/http/user' require 'travis/api/serialize/v2/http/validation_error' require 'travis/api/serialize/v2/http/ssh_key' +require 'travis/api/serialize/v2/http/account_env_var' diff --git a/lib/travis/api/serialize/v2/http/account_env_var.rb b/lib/travis/api/serialize/v2/http/account_env_var.rb new file mode 100644 index 000000000..26ed84d92 --- /dev/null +++ b/lib/travis/api/serialize/v2/http/account_env_var.rb @@ -0,0 +1,19 @@ +module Travis + module Api + module Serialize + module V2 + module Http + class AccountEnvVar < Travis::Api::Serialize::ObjectSerializer + attributes :id, :owner_id, :owner_type, :name, :value, :public, :created_at, :updated_at + + def serializable_hash(adapter_options) + hash = super(adapter_options) + hash.delete :value unless object.public? + hash + end + end + end + end + end + end +end diff --git a/lib/travis/api/v3/models/account_env_var.rb b/lib/travis/api/v3/models/account_env_var.rb new file mode 100644 index 000000000..0ca57c97c --- /dev/null +++ b/lib/travis/api/v3/models/account_env_var.rb @@ -0,0 +1,19 @@ +module Travis::API::V3 + class Models::AccountEnvVar < Model + belongs_to :owner, polymorphic: true + + serialize :value, Travis::Model::EncryptedColumn.new + + def save_account_env_var!(owner_type, owner_id, name, value, public) + self.owner_type = owner_type + self.owner_id = owner_id + self.name = name + self.value = value + self.public = public + + self.save! if self.valid? + + self + end + end +end diff --git a/lib/travis/api/v3/models/organization.rb b/lib/travis/api/v3/models/organization.rb index ff0c3e1fb..8be188a0d 100644 --- a/lib/travis/api/v3/models/organization.rb +++ b/lib/travis/api/v3/models/organization.rb @@ -43,8 +43,11 @@ def ensure_preferences end def custom_keys - return @custom_keys if defined? @custom_keys - @custom_keys = Models::CustomKey.where(owner_type: 'Organization', owner_id: id) + @custom_keys ||= Models::CustomKey.where(owner_type: 'Organization', owner_id: id) + end + + def account_env_vars + @account_env_vars ||= Models::AccountEnvVar.where(owner_type: 'Organization', owner_id: id) end alias members users diff --git a/lib/travis/api/v3/models/user.rb b/lib/travis/api/v3/models/user.rb index 54df28c54..1dd2d9043 100644 --- a/lib/travis/api/v3/models/user.rb +++ b/lib/travis/api/v3/models/user.rb @@ -97,8 +97,12 @@ def ensure_preferences end def custom_keys - return @custom_keys if defined? @custom_keys - @custom_keys = Models::CustomKey.where(owner_type: 'User', owner_id: id) + @custom_keys ||= Models::CustomKey.where(owner_type: 'User', owner_id: id) end + + def account_env_vars + @account_env_vars ||= Models::AccountEnvVar.where(owner_type: 'User', owner_id: id) + end + end end diff --git a/lib/travis/api/v3/permissions/account_env_var.rb b/lib/travis/api/v3/permissions/account_env_var.rb new file mode 100644 index 000000000..6aca292f2 --- /dev/null +++ b/lib/travis/api/v3/permissions/account_env_var.rb @@ -0,0 +1,23 @@ +require 'travis/api/v3/permissions/generic' + +module Travis::API::V3 + class Permissions::AccountEnvVar < Permissions::Generic + def write? + object.owner_type == 'Organization' ? + authorizer.for_account(object.owner_id, 'account_settings_create') : + true + end + + def delete? + object.owner_type == 'Organization' ? + authorizer.for_account(object.owner_id, 'account_settings_delete') : + true + end + + private + + def organization_permissions + @organization_permissions ||= Permissions::Organization.new(access_control, {id: object.owner_id}) + end + end +end diff --git a/lib/travis/api/v3/queries/account_env_var.rb b/lib/travis/api/v3/queries/account_env_var.rb new file mode 100644 index 000000000..7c809868c --- /dev/null +++ b/lib/travis/api/v3/queries/account_env_var.rb @@ -0,0 +1,48 @@ +module Travis::API::V3 + class Queries::AccountEnvVar < Query + + def find(params) + Models::AccountEnvVar.where(owner_type: params['owner_type'], owner_id: params['owner_id']) + end + + def create(params, current_user) + raise UnprocessableEntity, "'#{params['name']}' environment variable already exists." unless Models::AccountEnvVar.where(name: params['name'], owner_id: params['owner_id'], owner_type: params['owner_type']).count.zero? + + env_var = Travis::API::V3::Models::AccountEnvVar.new.save_account_env_var!( + params['owner_type'], + params['owner_id'], + params['name'], + params['value'], + params['public'] + ) + + Travis::API::V3::Models::Audit.create!( + owner: current_user, + change_source: 'travis-api', + source: env_var, + source_changes: { + action: 'create', + fingerprint: env_var.id + } + ) + env_var + end + + def delete(params, current_user) + env_var = Travis::API::V3::Models::AccountEnvVar.find(params['id']) + Travis::API::V3::Models::Audit.create!( + owner: current_user, + change_source: 'travis-api', + source: env_var, + source_changes: { + action: 'delete', + name: env_var.name, + owner_type: env_var.owner_type, + owner_id: env_var.owner_id + } + ) + + env_var.destroy + end + end +end diff --git a/lib/travis/api/v3/renderer/account_env_var.rb b/lib/travis/api/v3/renderer/account_env_var.rb new file mode 100644 index 000000000..72d2baf53 --- /dev/null +++ b/lib/travis/api/v3/renderer/account_env_var.rb @@ -0,0 +1,6 @@ +module Travis::API::V3 + class Renderer::AccountEnvVar < ModelRenderer + representation :standard, :id, :owner_id, :owner_type, :name, :value, :public, :created_at, :updated_at + representation :minimal, :id, :owner_id, :owner_type, :name, :value, :public + end +end diff --git a/lib/travis/api/v3/renderer/owner.rb b/lib/travis/api/v3/renderer/owner.rb index ee5aae9a8..e5fb29aa4 100644 --- a/lib/travis/api/v3/renderer/owner.rb +++ b/lib/travis/api/v3/renderer/owner.rb @@ -6,7 +6,7 @@ class Renderer::Owner < ModelRenderer representation(:minimal, :id, :login, :name, :vcs_type, :ro_mode) representation(:standard, :id, :login, :name, :github_id, :vcs_id, :vcs_type, :avatar_url, :education, - :allow_migration, :allowance, :ro_mode, :custom_keys, :trial_allowed) + :allow_migration, :allowance, :ro_mode, :custom_keys, :trial_allowed, :account_env_vars) representation(:additional, :repositories, :installation, :trial_allowed) def initialize(model, **options) diff --git a/lib/travis/api/v3/routes.rb b/lib/travis/api/v3/routes.rb index 8b4c8e711..4407004bc 100644 --- a/lib/travis/api/v3/routes.rb +++ b/lib/travis/api/v3/routes.rb @@ -350,6 +350,16 @@ module Routes delete :delete end + hidden_resource :account_env_var do + route '/account_env_var' + post :create + end + + hidden_resource :account_env_var do + route '/account_env_var/{id}' + delete :delete + end + hidden_resource :storage do route '/storage/{id}' get :find diff --git a/lib/travis/api/v3/services.rb b/lib/travis/api/v3/services.rb index 8a727a23a..aa3fa5f13 100644 --- a/lib/travis/api/v3/services.rb +++ b/lib/travis/api/v3/services.rb @@ -66,6 +66,7 @@ module Services User = Module.new { extend Services } UserSetting = Module.new { extend Services } UserSettings = Module.new { extend Services } + AccountEnvVar = Module.new { extend Services } def result_type @result_type ||= name[/[^:]+$/].underscore.to_sym diff --git a/lib/travis/api/v3/services/account_env_var/create.rb b/lib/travis/api/v3/services/account_env_var/create.rb new file mode 100644 index 000000000..c8bc9110d --- /dev/null +++ b/lib/travis/api/v3/services/account_env_var/create.rb @@ -0,0 +1,22 @@ +module Travis::API::V3 + class Services::AccountEnvVar::Create < Service + params :owner_id, :owner_type, :name, :value, :public + result_type :account_env_var + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + + account_env_var = Travis::API::V3::Models::AccountEnvVar.new( + owner_type: params['owner_type'], + owner_id: params['owner_id'], + name: params['name'], + value: params['value'], + public: params['public'] + ) + + access_control.permissions(account_env_var).write? + + result query(:account_env_var).create(params, access_control.user) + end + end +end diff --git a/lib/travis/api/v3/services/account_env_var/delete.rb b/lib/travis/api/v3/services/account_env_var/delete.rb new file mode 100644 index 000000000..5829dabf9 --- /dev/null +++ b/lib/travis/api/v3/services/account_env_var/delete.rb @@ -0,0 +1,21 @@ +module Travis::API::V3 + class Services::AccountEnvVar::Delete < Service + params :id + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + + account_env_var = Travis::API::V3::Models::AccountEnvVar.find_by( + id: params['id'] + ) + + if account_env_var + access_control.permissions(account_env_var).delete! + query(:account_env_var).delete(params, access_control.user) + deleted + else + raise NotFound, "No matching environment variable found." + end + end + end +end diff --git a/lib/travis/model/organization.rb b/lib/travis/model/organization.rb index 421beb4cb..e8fdb4dda 100644 --- a/lib/travis/model/organization.rb +++ b/lib/travis/model/organization.rb @@ -8,6 +8,7 @@ class Organization < Travis::Model has_many :repositories, :as => :owner has_one :owner_group, as: :owner has_many :custom_keys, as: :owner + has_many :account_env_vars, as: :owner has_many :broadcasts, as: :recipient after_initialize do diff --git a/lib/travis/model/user.rb b/lib/travis/model/user.rb index 990f6c3d9..1365c089d 100644 --- a/lib/travis/model/user.rb +++ b/lib/travis/model/user.rb @@ -15,6 +15,7 @@ class User < Travis::Model has_many :emails, dependent: :destroy has_one :owner_group, as: :owner has_many :custom_keys, as: :owner + has_many :account_env_vars, as: :owner has_many :broadcasts, as: :recipient before_create :set_as_recent diff --git a/spec/v3/services/account_env_var/create_spec.rb b/spec/v3/services/account_env_var/create_spec.rb new file mode 100644 index 000000000..a67032370 --- /dev/null +++ b/spec/v3/services/account_env_var/create_spec.rb @@ -0,0 +1,39 @@ +describe Travis::API::V3::Services::CustomKeys::Create, set_app: true do + let(:user) { FactoryBot.create(:user) } + let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: 1) } + let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}", "Content-Type" => "application/json" }} + let(:options) do + { + 'owner_id' => user.id, + 'owner_type' => 'User', + 'name' => 'TEST_VAR', + 'value' => 'VAL', + 'public' => true + } + end + let(:parsed_body) { JSON.load(body) } + + describe "try creating a account env var without login" do + before { post('/v3/account_env_var', options) } + example { expect(parsed_body).to eql_json({ + "@type" => "error", + "error_type" => "login_required", + "error_message" => "login required" + })} + end + + describe "creating account env var" do + before { post('/v3/account_env_var', options, headers) } + example { expect(parsed_body.except("id", "@permissions")).to eql_json({ + "@type" => "account_env_var", + "@representation" => "standard", + 'owner_id' => user.id, + 'owner_type' => 'User', + "name" => "TEST_VAR", + "value" => "VAL", + "public" => true, + "created_at" => Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ'), + "updated_at" => Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ') + })} + end +end diff --git a/spec/v3/services/account_env_var/delete_spec.rb b/spec/v3/services/account_env_var/delete_spec.rb new file mode 100644 index 000000000..ba7af2505 --- /dev/null +++ b/spec/v3/services/account_env_var/delete_spec.rb @@ -0,0 +1,14 @@ +describe Travis::API::V3::Services::AccountEnvVar::Delete, set_app: true do + let(:user) { FactoryBot.create(:user) } + let(:account_env_var) { Travis::API::V3::Models::AccountEnvVar.new.save_account_env_var!('User', user.id, 'TEST_VAR', 'VAL', true) } + let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: 1) } + let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }} + let(:parsed_body) { JSON.load(body) } + + describe "deleting account env var by id" do + before { delete("/v3/account_env_var/#{account_env_var.id}", {}, headers) } + example { expect(last_response.status).to eq 204 } + example { expect(Travis::API::V3::Models::AccountEnvVar.where(id: account_env_var.id)).to be_empty } + example { expect(parsed_body).to be_nil } + end +end diff --git a/spec/v3/services/installation/find_spec.rb b/spec/v3/services/installation/find_spec.rb index 5100ccf6a..e78338be9 100644 --- a/spec/v3/services/installation/find_spec.rb +++ b/spec/v3/services/installation/find_spec.rb @@ -68,6 +68,7 @@ "id" => 1 }, "custom_keys" => [], + "account_env_vars" => [], "allow_migration" => false, "recently_signed_up" => false, "secure_user_hash" => nil, diff --git a/spec/v3/services/organization/find_spec.rb b/spec/v3/services/organization/find_spec.rb index b459ea535..fb70591b8 100644 --- a/spec/v3/services/organization/find_spec.rb +++ b/spec/v3/services/organization/find_spec.rb @@ -50,7 +50,8 @@ "@representation" => "minimal", "id" => org.id }, - "custom_keys" => [] + "custom_keys" => [], + "account_env_vars" => [] }} end @@ -101,7 +102,8 @@ "@representation" => "minimal", "id" => org.id }, - "custom_keys" => [] + "custom_keys" => [], + "account_env_vars" => [] }} end diff --git a/spec/v3/services/organizations/for_current_user_spec.rb b/spec/v3/services/organizations/for_current_user_spec.rb index dc936cec4..d0676ebb6 100644 --- a/spec/v3/services/organizations/for_current_user_spec.rb +++ b/spec/v3/services/organizations/for_current_user_spec.rb @@ -90,7 +90,8 @@ "@representation" => "minimal", "id" => org.id }, - "custom_keys" => [] + "custom_keys" => [], + "account_env_vars" => [] }] }} end diff --git a/spec/v3/services/owner/find_spec.rb b/spec/v3/services/owner/find_spec.rb index 8ee837a42..7742805e7 100644 --- a/spec/v3/services/owner/find_spec.rb +++ b/spec/v3/services/owner/find_spec.rb @@ -59,7 +59,8 @@ "@representation" => "minimal", "id" => org.id }, - "custom_keys" => [] + "custom_keys" => [], + "account_env_vars" => [] }} end @@ -102,7 +103,8 @@ "@representation" => "minimal", "id" => org.id }, - "custom_keys" => [] + "custom_keys" => [], + "account_env_vars" => [] }} end @@ -150,6 +152,7 @@ "id" => org.id }, "custom_keys" => [], + "account_env_vars" => [], "repositories" => [{ "@type" => "repository", "@href" => "/v3/repo/#{repo.id}", @@ -256,6 +259,7 @@ "id" => org.id }, "custom_keys" => [], + "account_env_vars" => [], "repositories" => [{ "@type" => "repository", "@href" => "/v3/repo/#{repo.id}", @@ -355,7 +359,8 @@ "@representation" => "minimal", "id" => org.id }, - "custom_keys" => [] + "custom_keys" => [], + "account_env_vars" => [] }} end @@ -403,6 +408,7 @@ "id" => org.id }, "custom_keys" => [], + "account_env_vars" => [], "@warnings" => [{ "@type" => "warning", "message" => "query parameter organization.id not safelisted, ignored", @@ -443,6 +449,7 @@ "id" => user.id }, "custom_keys" => [], + "account_env_vars" => [], "recently_signed_up"=>false, "secure_user_hash" => nil, "trial_allowed" => false, @@ -477,6 +484,7 @@ "id" => user.id }, "custom_keys" => [], + "account_env_vars" => [], "recently_signed_up"=>false, "secure_user_hash" => nil, "trial_allowed" => false, @@ -511,6 +519,7 @@ "id" => user.id }, "custom_keys" => [], + "account_env_vars" => [], "recently_signed_up"=>false, "secure_user_hash" => nil, "trial_allowed" => false, @@ -552,6 +561,7 @@ "id" => user.id }, "custom_keys" => [], + "account_env_vars" => [], "recently_signed_up"=>false, "secure_user_hash" => nil, "trial_allowed" => false, diff --git a/spec/v3/services/user/current_spec.rb b/spec/v3/services/user/current_spec.rb index 2f0745ce7..d1cbc5651 100644 --- a/spec/v3/services/user/current_spec.rb +++ b/spec/v3/services/user/current_spec.rb @@ -32,6 +32,7 @@ "id" => user.id }, "custom_keys" => [], + "account_env_vars" => [], "recently_signed_up"=>false, "secure_user_hash" => nil, "ro_mode" => true, diff --git a/spec/v3/services/user/find_spec.rb b/spec/v3/services/user/find_spec.rb index 49fd4412e..6f519bb11 100644 --- a/spec/v3/services/user/find_spec.rb +++ b/spec/v3/services/user/find_spec.rb @@ -47,6 +47,7 @@ "id" => user.id }, "custom_keys" => [], + "account_env_vars" => [], "recently_signed_up"=>false, "secure_user_hash" => nil, "ro_mode" => false, @@ -88,6 +89,7 @@ "id" => user.id }, "custom_keys" => [], + "account_env_vars" => [], "recently_signed_up"=>false, "secure_user_hash" => nil, "ro_mode" => false, @@ -129,6 +131,7 @@ "id" => user.id }, "custom_keys" => [], + "account_env_vars" => [], "recently_signed_up"=>false, "secure_user_hash" => nil, "ro_mode" => false, diff --git a/spec/v3/services/v2_subscription/executions_spec.rb b/spec/v3/services/v2_subscription/executions_spec.rb index c0677db81..4a72077bb 100644 --- a/spec/v3/services/v2_subscription/executions_spec.rb +++ b/spec/v3/services/v2_subscription/executions_spec.rb @@ -206,6 +206,7 @@ "id"=>1 }, "custom_keys" => [], + "account_env_vars" => [], "email"=>"sven@fuchs.com", "is_syncing"=>nil, "synced_at"=>nil,