From 07f60552728db7276ad24dafd1ff601ae49442d2 Mon Sep 17 00:00:00 2001
From: Valery Sizov <vsv2711@gmail.com>
Date: Fri, 9 Oct 2015 13:20:38 +0300
Subject: [PATCH] Invalidate stored service password if the endpoint URL is
 changed

---
 app/models/project_services/bamboo_service.rb |  7 +++
 .../project_services/teamcity_service.rb      |  7 +++
 app/models/service.rb                         |  9 +++
 .../project_services/bamboo_service_spec.rb   | 56 +++++++++++++++++++
 .../project_services/teamcity_service_spec.rb | 56 +++++++++++++++++++
 spec/models/service_spec.rb                   | 23 ++++++++
 6 files changed, 158 insertions(+)
 create mode 100644 spec/models/project_services/bamboo_service_spec.rb
 create mode 100644 spec/models/project_services/teamcity_service_spec.rb

diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index d8aedbd2ab4..5f5255ab487 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -40,12 +40,19 @@ class BambooService < CiService
   attr_accessor :response
 
   after_save :compose_service_hook, if: :activated?
+  before_update :reset_password
 
   def compose_service_hook
     hook = service_hook || build_service_hook
     hook.save
   end
 
+  def reset_password
+    if prop_updated?(:bamboo_url)
+      self.password = nil
+    end
+  end
+
   def title
     'Atlassian Bamboo CI'
   end
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
index 3c002a1634b..fb11cad352e 100644
--- a/app/models/project_services/teamcity_service.rb
+++ b/app/models/project_services/teamcity_service.rb
@@ -37,12 +37,19 @@ class TeamcityService < CiService
   attr_accessor :response
 
   after_save :compose_service_hook, if: :activated?
+  before_update :reset_password
 
   def compose_service_hook
     hook = service_hook || build_service_hook
     hook.save
   end
 
+  def reset_password
+    if prop_updated?(:teamcity_url)
+      self.password = nil
+    end
+  end
+
   def title
     'JetBrains TeamCity CI'
   end
diff --git a/app/models/service.rb b/app/models/service.rb
index 60fcc9d2857..7e845d565b1 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -117,6 +117,15 @@ class Service < ActiveRecord::Base
     end
   end
 
+  # ActiveRecord does not provide a mechanism to track changes in serialized keys.
+  # This is why we need to perform extra query to do it mannually.
+  def prop_updated?(prop_name)
+    relation_name = self.type.underscore
+    previous_value = project.send(relation_name).send(prop_name)
+    return false if previous_value.nil?
+    previous_value != send(prop_name)
+  end
+
   def async_execute(data)
     return unless supported_events.include?(data[:object_kind])
 
diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb
new file mode 100644
index 00000000000..f8a3493f52d
--- /dev/null
+++ b/spec/models/project_services/bamboo_service_spec.rb
@@ -0,0 +1,56 @@
+# == Schema Information
+#
+# Table name: services
+#
+#  id                    :integer          not null, primary key
+#  type                  :string(255)
+#  title                 :string(255)
+#  project_id            :integer
+#  created_at            :datetime
+#  updated_at            :datetime
+#  active                :boolean          default(FALSE), not null
+#  properties            :text
+#  template              :boolean          default(FALSE)
+#  push_events           :boolean          default(TRUE)
+#  issues_events         :boolean          default(TRUE)
+#  merge_requests_events :boolean          default(TRUE)
+#  tag_push_events       :boolean          default(TRUE)
+#  note_events           :boolean          default(TRUE), not null
+#
+
+require 'spec_helper'
+
+describe BambooService, models: true do
+  describe "Associations" do
+    it { is_expected.to belong_to :project }
+    it { is_expected.to have_one :service_hook }
+  end
+
+  describe "Execute" do
+    let(:user)    { create(:user) }
+    let(:project) { create(:project) }
+
+    before do
+      @bamboo_service = BambooService.create(
+        project: create(:project),
+        properties: {
+          bamboo_url: 'http://gitlab.com',
+          username: 'mic',
+          password: "password"
+        }
+      )
+    end
+
+    it "reset password if url changed" do
+      @bamboo_service.bamboo_url = 'http://gitlab1.com'
+      @bamboo_service.save
+      expect(@bamboo_service.password).to be_nil
+    end
+
+    it "does not reset password if username changed" do
+      @bamboo_service.username = "some_name"
+      @bamboo_service.save
+      expect(@bamboo_service.password).to eq("password")
+    end
+  end
+end
diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb
new file mode 100644
index 00000000000..3dbd2346bcc
--- /dev/null
+++ b/spec/models/project_services/teamcity_service_spec.rb
@@ -0,0 +1,56 @@
+# == Schema Information
+#
+# Table name: services
+#
+#  id                    :integer          not null, primary key
+#  type                  :string(255)
+#  title                 :string(255)
+#  project_id            :integer
+#  created_at            :datetime
+#  updated_at            :datetime
+#  active                :boolean          default(FALSE), not null
+#  properties            :text
+#  template              :boolean          default(FALSE)
+#  push_events           :boolean          default(TRUE)
+#  issues_events         :boolean          default(TRUE)
+#  merge_requests_events :boolean          default(TRUE)
+#  tag_push_events       :boolean          default(TRUE)
+#  note_events           :boolean          default(TRUE), not null
+#
+
+require 'spec_helper'
+
+describe TeamcityService, models: true do
+  describe "Associations" do
+    it { is_expected.to belong_to :project }
+    it { is_expected.to have_one :service_hook }
+  end
+
+  describe "Execute" do
+    let(:user)    { create(:user) }
+    let(:project) { create(:project) }
+
+    before do
+      @teamcity_service = TeamcityService.create(
+        project: create(:project),
+        properties: {
+          teamcity_url: 'http://gitlab.com',
+          username: 'mic',
+          password: "password"
+        }
+      )
+    end
+
+    it "reset password if url changed" do
+      @teamcity_service.teamcity_url = 'http://gitlab1.com'
+      @teamcity_service.save
+      expect(@teamcity_service.password).to be_nil
+    end
+
+    it "does not reset password if username changed" do
+      @teamcity_service.username = "some_name"
+      @teamcity_service.save
+      expect(@teamcity_service.password).to eq("password")
+    end
+  end
+end
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index a213ffe6c4b..da87ea5b84f 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -103,4 +103,27 @@ describe Service do
       end
     end
   end
+
+  describe "#prop_updated?" do
+    let(:service) do
+      BambooService.create(
+        project: create(:project),
+        properties: {
+          bamboo_url: 'http://gitlab.com',
+          username: 'mic',
+          password: "password"
+        }
+      )
+    end
+
+    it "returns false" do
+      service.username = "key_changed"
+      expect(service.prop_updated?(:bamboo_url)).to be_falsy
+    end
+
+    it "returns true" do
+      service.bamboo_url = "http://other.com"
+      expect(service.prop_updated?(:bamboo_url)).to be_truthy
+    end
+  end
 end
-- 
GitLab