Rubygem 項目敏感數據的管理

既然要說 Ruby 那肯定是離不開 Rails 的,畢竟

Ruby 只是 Ruby on Rails 的一套框架,才不是什么程序語言呢!

話不多說先新建一個 Rails 項目:

$ rails new app --B

默認情況下,Rails 提供了三個環境:development,test 和 production,其定義是在 app/config/environments 目錄下,以后的配置都是要考慮這三個環境的(手動修改過相關配置的可能不一定是三個)。重點要說的是 app/config/secrets.yml 這個文件

# app/config/secrets.yml

development:
  secret_key_base: 5734127d4e3ebf07d9d7af9aed02b869448faf4afefc7bb7abbfdb9979ed92546ee84edbc37e0e50b5469c0d43923faf2d2d9e46d5f5f0d1d997f47b656dbd45

test:
  secret_key_base: 35469501039e3c1abc7f4b76d8184df2c6ed0491a6b4f522be84ae5c52d4cf7e5ef31e50f5f063794d076d25e78ce76e8533e72ea20171193c59de861e861b1a

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

其中 production 的值是 <%= ENV["SECRET_KEY_BASE"] %> (至于為什么 yaml 文件中可以使用ERB,可以點這個鏈接查看原因下面也會提到一點具體代碼),那么問題就來了,這個 ENV 到底是個啥子東東呢?

ruby-doc 上是這么解釋的:

ENV is a hash-like accessor for environment variables.

由此可知,生產環境下的這個 secret_key_base 是一個很 隱私? 私密?機密? 敏感?的數據,不能讓其出現在代碼倉庫之中,而要將其放在環境變量 ENV 中(很多非 Rails 項目,更準確的講是 Rubygem 項目都沒有注意到這個,就我個人而言,即使是測試環境下的外部測試帳號信息也是敏感數據)。具體做法 Railscast 上也有一節是專門講這個的(不過好像是 revised 的)。

那么,在 Ruby 項目中這又是如何實現的呢?

顯然,純 Ruby 項目沒有 Rails 考慮的那么周到,從 Rails 到 Ruby 其感覺就像是一夜回到解放前,不過自從有 bundle 工具之后,起碼可以說回到解放后了。

下面以 JPush API Ruby Client (這是一個 gem 包,即為 Ruby SDK,為方便起見,下文統一稱為 SDK)為例來說道說道。

首先細化一下這個 SDK 的使用場景:有一個使用 Rails 作為后臺的項目,現在需要使用極光推送為手機客戶端提供消息推送服務,那么在服務端就需要集成 JPush 的 Ruby SDK。首先要安裝這個 gem,得益于 bundle 工具,只需要在 Gemfile 里面添加相應的配置即可:

# Gemfile
gem 'jpush'

然后運行

$ bundle install

DONE!

JPush SDK 便已經成功安裝到 app 項目中。

在 Rails 項目中有三個環境,對于不同的環境可能需要給 SDK 不同的帳號信息。比如微信就存在微信公眾平臺接口測試帳號一說,不過很多其他的服務(包括極光推送)并不存在測試帳號一說。不論如何去做,可以確定的是,處于生產環境中的 SDK 的敏感信息可以交由 Rails (即 使用這個 SDK 的項目本身)來管理,并不需要 SDK 來做,但是在 SDK 開發測試過程中的敏感信息就要 SDK 自己來管理了。

根據以上的結論,在 SDK 項目的代碼里面應該有一個下面這樣的類

module JPush
 class Client
    def initialize(app_key, master_secret)
      xxx
    end
  end
end

來管理賬戶信息,這個類的實例化,留給使用這個 SDK 的開發者在自己的項目中去做。

在單元測試中,需要在 test_helper.rb 中實例化這個 Client 類,但是帳號信息哪里來,這是個問題。在這里有幾種方案(偷師 Rails):

  • 設置系統級環境變量,然后從 ENV 中讀取
  • 將需要的變量寫入一個 yaml 文件,在初始化測試的時候寫進 ENV 中,再讀取

等等,既然可以寫進 yaml 文件中,Ruby 本身又能方便的處理 yaml,那為什么還要寫進 ENV 里面呢。

所以結論是新建一個 config.yml 將需要填寫的信息模板寫進去,比如:

# test/config.yml.example
app_key: APP_KEY
master_secret: MASTER_SECRET
tags:
  tag0: TAG_0
  tag1: TAG_1

然后在 test_helper.rb 中處理

# test/test_helper.rb
# symbolize_keys 是在其他地方實現的解析嵌套 Hash 的方法

cnf =
  if File.exists? cnf_file = File.expand_path('../config.yml', __FILE__)
    require "yaml"
    YAML.load_file(cnf_file).symbolize_keys
  else
    raise 'No Config File Found!!'
  end

app_key = cnf[:app_key]
master_secret = cnf[:master_secret]

@@client = JPush::Client.new(app_key, master_secret)

當然這里省略了一些細節。需要把 config.yml 加入到 .gitignore 中,在項目中只能有一個 config.yml.example 文件,這樣的話,參與開發的流程就應該是

  • fork 項目
  • $ cp test/config.yml.example test/config.yml
  • 編輯 test/config.yml ,填寫所需的信息
  • $ bundle exec rake test

然而,持續集成那邊 Travis CI build 出錯,原因也很清楚,可以寫腳本讓其復制 config.yml 但是無法做到填寫數據。各種數據都是 example 的數據,沒有任何意義,好在 Travis 可以為其設置環境變量,這樣的話,只要稍微改一改就可以完成。因此上面說的那句話

Ruby 本身又能方便的處理 yaml,那為什么還要寫進 ENV 里面呢

我收回。

# test/config.yml
app_key: <%= APP_KEY %>
master_secret: <%= MASTER_SECRET %>
tags:
  tag0: <%= TAG_0 %>
  tag1: <%= TAG_1 %>
# test/test_helper.rb
# symbolize_keys 是在其他地方實現的解析嵌套 Hash 的方法

cnf =
  if File.exists? cnf_file = File.expand_path('../config.yml', __FILE__)
    require "yaml"
    require "erb"
    template = File.read(cnf_file)
    erb_result = ERB.new(template).result
    YAML.load(erb_result).symbolize_keys
  else
    raise 'No Config File Found!!'
  end

這樣的話,開發流程其實并沒有怎么變,只不過多了一個選擇,可以選擇編輯 test/config.yml 或者設置相應的環境變量,關于 Tiavis CI 的話,只需添加相應的環境變量,便能順利的構建。

OVER

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容