Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Google Private Key accessible from the ENV. #6063

Merged
merged 8 commits into from
May 24, 2023
4 changes: 2 additions & 2 deletions .dassie/config/analytics.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ analytics:
analytics_id: <%= ENV['GOOGLE_ANALYTICS_ID'] %>
app_name: <%= ENV['GOOGLE_OAUTH_APP_NAME'] %>
app_version: <%= ENV['GOOGLE_OAUTH_APP_VERSION'] %>
privkey_value: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_VALUE'] %>
privkey_path: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_PATH'] %>
privkey_secret: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_SECRET'] %>
client_email: <%= ENV['GOOGLE_OAUTH_CLIENT_EMAIL'] %>
matomo:
matomo:
base_url: <%= ENV['MATOMO_BASE_URL'] %>
site_id: <%= ENV['MATOMO_SITE_ID'] %>
auth_token: <%= ENV['MATOMO_AUTH_TOKEN'] %>

1 change: 1 addition & 0 deletions .koppie/config/analytics.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ analytics:
analytics_id: <%= ENV['GOOGLE_ANALYTICS_ID'] %>
app_name: <%= ENV.fetch('GOOGLE_OAUTH_APP_NAME', 'nurax-pg') %>
app_version: <%= ENV.fetch['GOOGLE_OAUTH_APP_VERSION'] %>
privkey_value: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_VALUE'] %>
privkey_path: <%= ENV.fetch['GOOGLE_OAUTH_PRIVATE_KEY_PATH'] %>
privkey_secret: <%= ENV.fetch['GOOGLE_OAUTH_PRIVATE_KEY_SECRET'] %>
client_email: <%= ENV.fetch['GOOGLE_OAUTH_CLIENT_EMAIL'] %>
Expand Down
2 changes: 1 addition & 1 deletion .regen
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# When updating CI regen seed, set to current date.
2023-05-15T10:04:07
2023-05-24T10:43:07
38 changes: 23 additions & 15 deletions app/services/hyrax/analytics/google.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
# frozen_string_literal: true

require 'oauth2'
require 'signet/oauth_2/client'

# rubocop:disable Metrics/ModuleLength
module Hyrax
module Analytics
# rubocop:disable Metrics/ModuleLength
module Google
extend ActiveSupport::Concern
# rubocop:disable Metrics/BlockLength
class_methods do
# Loads configuration options from config/analytics.yml. Expected structure:
# Loads configuration options from config/analytics.yml. You only need PRIVATE_KEY_PATH or
# PRIVATE_KEY_VALUE. VALUE takes precedence.
# Expected structure:
# `analytics:`
# ` google:`
# ` app_name: <%= ENV['GOOGLE_OAUTH_APP_NAME']`
# ` app_version: <%= ENV['GOOGLE_OAUTH_APP_VERSION']`
# ` privkey_path: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_PATH']`
# ` privkey_value: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_VALUE']`
# ` privkey_secret: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_SECRET']`
# ` client_email: <%= ENV['GOOGLE_OAUTH_CLIENT_EMAIL']`
# @return [Config]
Expand All @@ -41,26 +45,25 @@ def self.load_from_yaml
new config
end

REQUIRED_KEYS = %w[analytics_id app_name app_version privkey_path privkey_secret client_email].freeze
KEYS = %w[analytics_id app_name app_version privkey_path privkey_value privkey_secret client_email].freeze
REQUIRED_KEYS = %w[analytics_id app_name app_version privkey_secret client_email].freeze

def initialize(config)
@config = config
end

# @return [Boolean] are all the required values present?
def valid?
config_keys = @config.keys
REQUIRED_KEYS.all? { |required| config_keys.include?(required) }
end
return false unless @config['privkey_value'].present? || @config['privkey_path'].present?

REQUIRED_KEYS.each do |key|
class_eval %{ def #{key}; @config.fetch('#{key}'); end }
REQUIRED_KEYS.all? { |required| @config[required].present? }
end

# This method allows setting the analytics id in the initializer
# @deprecated set the analytics id in either ENV['GOOGLE_ANALYTICS_ID'] or config/analytics.yaml
def analytics_id=(value)
@config['analytics_id'] = value
KEYS.each do |key|
# rubocop:disable Style/EvalWithLocation
class_eval %{ def #{key}; @config.fetch('#{key}'); end }
class_eval %{ def #{key}=(value); @config['#{key}'] = value; end }
# rubocop:enable Style/EvalWithLocation
end
end

Expand All @@ -77,8 +80,12 @@ def oauth_client
end

def auth_client(scope)
raise "Private key file for Google analytics was expected at '#{config.privkey_path}', but no file was found." unless File.exist?(config.privkey_path)
private_key = File.read(config.privkey_path)
private_key = Base64.decode64(config.privkey_value) if config.privkey_value.present?
if private_key.blank?
raise "Private key file for Google analytics was expected at '#{config.privkey_path}', but no file was found." unless File.exist?(config.privkey_path)

private_key = File.read(config.privkey_path)
end
Signet::OAuth2::Client.new token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
audience: 'https://accounts.google.com/o/oauth2/token',
scope: scope,
Expand Down Expand Up @@ -123,7 +130,7 @@ def to_date_range(period)

[start_date, end_date]
end
# rubocop:enabl e Metrics/MethodLength
# rubocop:enable Metrics/MethodLength

def keyword_conversion(date)
case date
Expand Down Expand Up @@ -199,6 +206,7 @@ def total_visitors(period = 'month', date = default_date_range)
end
# rubocop:enable Metrics/BlockLength
end
# rubocop:enable Metrics/ModuleLength
end
end
# rubocop:enable Metrics/ModuleLength
7 changes: 4 additions & 3 deletions app/services/hyrax/analytics/matomo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ module Matomo
# Loads configuration options from config/analytics.yml. Expected structure:
# `analytics:`
# ` matomo:`
# ` base_url: <%= ENV['MATOMOT_BASE_URL']`
# ` site_id: <%= ENV['MATOMOT_SITE_ID']`
# ` auth_token: <%= ENV['MATOMOT_AUTH_TOKEN']`
# ` base_url: <%= ENV['MATOMO_BASE_URL']`
# ` site_id: <%= ENV['MATOMO_SITE_ID']`
# ` auth_token: <%= ENV['MATOMO_AUTH_TOKEN']`
# @return [Config]
def config
@config ||= Config.load_from_yaml
end

class Config
# TODO: test matomo and see if it needs any of the updates from https://github.com/samvera/hyrax/pull/6063
def self.load_from_yaml
filename = Rails.root.join('config', 'analytics.yml')
yaml = YAML.safe_load(ERB.new(File.read(filename)).result)
Expand Down
1 change: 1 addition & 0 deletions lib/generators/hyrax/templates/config/analytics.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ analytics:
analytics_id: <%= ENV['GOOGLE_ANALYTICS_ID'] %>
app_name: <%= ENV['GOOGLE_OAUTH_APP_NAME'] %>
app_version: <%= ENV['GOOGLE_OAUTH_APP_VERSION'] %>
privkey_value: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_VALUE'] %>
privkey_path: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_PATH'] %>
privkey_secret: <%= ENV['GOOGLE_OAUTH_PRIVATE_KEY_SECRET'] %>
client_email: <%= ENV['GOOGLE_OAUTH_CLIENT_EMAIL'] %>
Expand Down
16 changes: 10 additions & 6 deletions spec/lib/hyrax/analytics_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
RSpec.describe Hyrax::Analytics do
before do
ENV['GOOGLE_ANALYTICS_ID'] = 'UA-XXXXXXXX'
ENV['GOOGLE_OAUTH_APP_NAME'] = "My App Name"
ENV['GOOGLE_OAUTH_APP_VERSION'] = "0.0.1"
ENV['GOOGLE_OAUTH_PRIVATE_KEY_PATH'] = "/tmp/privkey.p12"
ENV['GOOGLE_OAUTH_PRIVATE_KEY_SECRET'] = "s00pers3kr1t"
ENV['GOOGLE_OAUTH_CLIENT_EMAIL'] = "oauth@example.org"
ENV['GOOGLE_OAUTH_APP_NAME'] = 'My App Name'
ENV['GOOGLE_OAUTH_APP_VERSION'] = '0.0.1'
ENV['GOOGLE_OAUTH_PRIVATE_KEY_PATH'] = '/tmp/privkey.p12'
ENV['GOOGLE_OAUTH_PRIVATE_KEY_VALUE'] = ''
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added this line. the rest of the env changes are unifying them with single quotes.

ENV['GOOGLE_OAUTH_PRIVATE_KEY_SECRET'] = 's00pers3kr1t'
ENV['GOOGLE_OAUTH_CLIENT_EMAIL'] = 'oauth@example.org'

described_class.send(:remove_instance_variable, :@config) if described_class.send(:instance_variable_defined?, :@config)
end
Expand All @@ -23,6 +24,7 @@
expect(config.analytics_id).to eql 'UA-XXXXXXXX'
expect(config.app_name).to eql 'My App Name'
expect(config.app_version).to eql '0.0.1'
expect(config.privkey_value).to be_nil
expect(config.privkey_path).to eql '/tmp/privkey.p12'
expect(config.privkey_secret).to eql 's00pers3kr1t'
expect(config.client_email).to eql 'oauth@example.org'
Expand All @@ -35,6 +37,7 @@
analytics:
app_name: My App Name
app_version: 0.0.1
privkey_value:
privkey_path: /tmp/privkey.p12
privkey_secret: s00pers3kr1t
client_email: oauth@example.org
Expand All @@ -44,6 +47,7 @@
it 'reads its config from a yaml file' do
expect(config.app_name).to eql 'My App Name'
expect(config.app_version).to eql '0.0.1'
expect(config.privkey_value).to be_nil
expect(config.privkey_path).to eql '/tmp/privkey.p12'
expect(config.privkey_secret).to eql 's00pers3kr1t'
expect(config.client_email).to eql 'oauth@example.org'
Expand Down Expand Up @@ -76,7 +80,7 @@
describe "#profile" do
subject { described_class.profile }

context "when the private key file is missing" do
context "when the private key file and private key value are missing" do
it "raises an error" do
expect { subject }.to raise_error RuntimeError, "Private key file for Google analytics was expected at '/tmp/privkey.p12', but no file was found."
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ def add_analytics_config
analytics_id: UA-XXXXXXXX
app_name: My App Name
app_version: 0.0.1
privkey_value:
privkey_path: /tmp/privkey.p12
privkey_secret: s00pers3kr1t
client_email: oauth@example.org
Expand Down