Skip to content

Commit

Permalink
Extract the apple key out from the config (#713)
Browse files Browse the repository at this point in the history
* Extract the apple key out from the config

* Remove unused

* References the key attribute

* Defer to the factory

* Exclude the apple_keys table

* Fix flickering test
  • Loading branch information
svevang authored Jul 27, 2023
1 parent 11181e0 commit 042957d
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 98 deletions.
8 changes: 4 additions & 4 deletions app/models/apple/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ def self.from_env
end

def self.from_apple_config(apple_config)
if apple_config.no_apple_credentials?
if apple_config.key.blank?
Rails.logger.info("No Apple API keys in config object, falling back to environment default keys",
{apple_credential_id: apple_config.id,
podcast_id: apple_config.podcast_id,
podcast_title: apple_config.podcast_title})
from_env
else
new(provider_id: apple_config.apple_provider_id,
key_id: apple_config.apple_key_id,
key: apple_config.apple_key)
new(provider_id: apple_config.key.provider_id,
key_id: apple_config.key.key_id,
key: apple_config.key.key_pem)
end
end

Expand Down
34 changes: 9 additions & 25 deletions app/models/apple/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,30 @@ class Config < ApplicationRecord
belongs_to :public_feed, class_name: "Feed"
belongs_to :private_feed, class_name: "Feed"

belongs_to :key, class_name: "Apple::Key", optional: true

has_one :podcast, through: :public_feed

delegate :title, to: :podcast, prefix: "podcast"
delegate :id, to: :podcast, prefix: "podcast"

delegate :provider_id, to: :key
delegate :key_id, to: :key
delegate :key_pem, to: :key
delegate :key_pem_b64, to: :key

validates_presence_of :public_feed
validates_presence_of :private_feed

validates_associated :public_feed
validates_associated :private_feed
validates_presence_of :apple_provider_id, if: :any_apple_credentials_exist?
validates_presence_of :apple_key_id, if: :any_apple_credentials_exist?
validates_presence_of :apple_key_pem_b64, if: :any_apple_credentials_exist?

validates :public_feed, uniqueness: {scope: :private_feed,
message: "can only have one credential per public and private feed"}
validates :public_feed, exclusion: {in: ->(apple_credential) { [apple_credential.private_feed] }}

validate :apple_provider_id_is_valid, if: :apple_provider_id?

def apple_provider_id_is_valid
# ensure that it does not have an underscore
if apple_provider_id.include?("_")
errors.add(:apple_provider_id, "cannot contain an underscore")
end
end

def publish_to_apple?
return false unless apple_credentials?
return false unless key&.valid?

public_feed.publish_to_apple?(self)
end
Expand All @@ -41,18 +37,6 @@ def build_publisher
Apple::Publisher.from_apple_config(self)
end

def apple_credentials?
apple_provider_id.present? && apple_key_id.present? && apple_key_pem_b64.present?
end

def any_apple_credentials_exist?
apple_provider_id.present? || apple_key_id.present? || apple_key_pem_b64.present?
end

def no_apple_credentials?
!any_apple_credentials_exist?
end

def apple_key
Base64.decode64(apple_key_pem_b64)
end
Expand Down
21 changes: 21 additions & 0 deletions app/models/apple/key.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module Apple
class Key < ApplicationRecord
validates_presence_of :provider_id
validates_presence_of :key_id
validates_presence_of :key_pem_b64

validate :provider_id_is_valid, if: :provider_id?

def provider_id_is_valid
if provider_id.include?("_")
errors.add(:provider_id, "cannot contain an underscore")
end
end

def key_pem
Base64.decode64(key_pem_b64)
end
end
end
42 changes: 42 additions & 0 deletions db/migrate/20230719174404_extract_credentials.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
class ExtractCredentials < ActiveRecord::Migration[7.0]
def change
create_table :apple_keys do |t|
t.string :provider_id
t.string :key_id
t.text :key_pem_b64
t.timestamps
end

add_reference :apple_configs, :key, index: true

reversible do |direction|
direction.up do
Apple::Config.distinct.pluck(:apple_provider_id, :apple_key_id, :apple_key_pem_b64).each do |(apple_provider_id, apple_key_id, apple_key_pem_b64)|
cred = Apple::Key.create!(
provider_id: apple_provider_id,
key_id: apple_key_id,
key_pem_b64: apple_key_pem_b64
)

Apple::Config.where(apple_provider_id: apple_provider_id, apple_key_id: apple_key_id, apple_key_pem_b64: apple_key_pem_b64).each do |config|
config.update!(key_id: cred.id)
end
end
end

direction.down do
Apple::Config.all.each do |config|
config.update!(
apple_key_id: config.key.key_id,
apple_provider_id: config.key.provider_id,
apple_key_pem_b64: config.key.key_pem_b64
)
end
end
end

remove_column :apple_configs, :apple_provider_id, :text
remove_column :apple_configs, :apple_key_id, :text
remove_column :apple_configs, :apple_key_pem_b64, :text
end
end
15 changes: 11 additions & 4 deletions db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion ops/bin/dump_db.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ time pg_dump --verbose -Fc -h 127.0.0.1 \
--exclude-table-data 'public.say_when_job_executions' \
--exclude-table-data 'sessions' \
--exclude-table-data 'tasks' \
--exclude-table-data 'apple_keys' \
-W -d "${DUMP_REMOTE_POSTGRES_DATABASE}" -U "$DUMP_REMOTE_POSTGRES_USER" -f "$OUTPUT_FILE"
echo "Wrote: $OUTPUT_FILE"

Expand All @@ -59,4 +60,4 @@ echo ""
echo "DONE"
echo ""
echo "Now run 'setup_clone_db.sh' followed by 'load_db.sh'"
echo ""
echo ""
4 changes: 1 addition & 3 deletions test/factories/apple_config_factory.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
FactoryBot.define do
factory :apple_config, class: Apple::Config do
apple_key_id { "some_key_id" }
apple_key_pem_b64 { Base64.encode64(test_file("/fixtures/apple_podcasts_connect_keyfile.pem")) }
apple_provider_id { SecureRandom.uuid }
publish_enabled { true }
sync_blocks_rss { true }
key { build(:apple_key) }

transient do
podcast { build(:podcast) }
Expand Down
7 changes: 7 additions & 0 deletions test/factories/apple_key_factory.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FactoryBot.define do
factory :key, class: Apple::Key, aliases: [:apple_key] do
key_id { "some_key_id" }
key_pem_b64 { Base64.encode64(test_file("/fixtures/apple_podcasts_connect_keyfile.pem")) }
provider_id { SecureRandom.uuid }
end
end
8 changes: 4 additions & 4 deletions test/models/apple/api_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,13 @@
creds = build(:apple_config)
api = Apple::Api.from_apple_config(creds)

assert_equal api.provider_id, creds.apple_provider_id
assert_equal api.key_id, creds.apple_key_id
assert_equal api.key, creds.apple_key
assert_equal api.provider_id, creds.provider_id
assert_equal api.key_id, creds.key_id
assert_equal api.key, creds.key_pem
end

it "falls back on the environment if the apple credential attributes are not set" do
creds = create(:apple_config, apple_provider_id: nil, apple_key_id: nil, apple_key_pem_b64: nil)
creds = create(:apple_config, key: nil)
api = Apple::Api.from_apple_config(creds)
assert_equal api.key_id, "apple key id from env"

Expand Down
51 changes: 0 additions & 51 deletions test/models/apple/config_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,6 @@
refute c3.valid?
end

it "requires apple key fields" do
pub = create(:feed, private: false)
priv = create(:feed, private: true)
c = build(:apple_config, public_feed: pub, private_feed: priv)
assert c.valid?

c.apple_key_id = nil
refute c.valid?

c.apple_key_id = "pears are better"
c.apple_key_pem_b64 = nil
refute c.valid?
end

it "is unique to a public and private feed" do
f1 = create(:feed)
f2 = create(:feed)
Expand All @@ -51,42 +37,5 @@
c5 = build(:apple_config, public_feed: f1, private_feed: f1)
refute c5.valid?
end

it "requires all apple credentials to have a value or be nil" do
f1 = create(:feed)
f2 = create(:feed)

v1 = build(:apple_config, public_feed: f1, private_feed: f2, apple_provider_id: nil, apple_key_id: "blood",
apple_key_pem_b64: "orange")
refute v1.valid?

v2 = build(:apple_config, public_feed: f1, private_feed: f2, apple_provider_id: "barlett", apple_key_id: nil,
apple_key_pem_b64: "pear")
refute v2.valid?

v3 = build(:apple_config, public_feed: f1, private_feed: f2, apple_provider_id: "cotton candy",
apple_key_id: "grapes", apple_key_pem_b64: nil)
refute v3.valid?

v4 = build(:apple_config, public_feed: f1, private_feed: f2, apple_provider_id: nil, apple_key_id: nil,
apple_key_pem_b64: nil)
assert v4.valid?
end

it "requires the apple provider id to not have an underscore" do
f1 = create(:feed)
f2 = create(:feed)

v1 = build(:apple_config, public_feed: f1, private_feed: f2, apple_provider_id: "foo_bar")
refute v1.valid?
assert_equal ["cannot contain an underscore"], v1.errors[:apple_provider_id]
end
end

describe "apple_key" do
it "base64 decodes the apple key" do
c = Apple::Config.new(apple_key_pem_b64: Base64.encode64("hello"))
assert_equal c.apple_key, "hello"
end
end
end
32 changes: 32 additions & 0 deletions test/models/apple/key_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
require "test_helper"

describe Apple::Config do
describe "#valid?" do
it "requires all apple credentials to have a value or be nil" do
v1 = build(:apple_key, provider_id: nil, key_id: "blood", key_pem_b64: "orange")
refute v1.valid?

v2 = build(:apple_key, provider_id: "barlett", key_id: nil, key_pem_b64: "pear")
refute v2.valid?

v3 = build(:apple_key, provider_id: "cotton candy", key_id: "grapes", key_pem_b64: nil)
refute v3.valid?

v4 = build(:apple_key, provider_id: nil, key_id: nil, key_pem_b64: nil)
refute v4.valid?
end

it "requires the apple provider id to not have an underscore" do
v1 = build(:apple_key, provider_id: "foo_bar")
refute v1.valid?
assert_equal ["cannot contain an underscore"], v1.errors[:provider_id]
end
end

describe "apple_key" do
it "base64 decodes the apple key" do
c = Apple::Key.new(key_pem_b64: Base64.encode64("hello"))
assert_equal c.key_pem, "hello"
end
end
end
8 changes: 2 additions & 6 deletions test/models/publishing_pipeline_state_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -247,16 +247,12 @@
create(:apple_config,
public_feed: f1,
private_feed: f2,
publish_enabled: true,
apple_key_id: "valencia",
apple_key_pem_b64: "orange")
publish_enabled: true)

create(:apple_config,
public_feed: f1,
private_feed: f3,
publish_enabled: true,
apple_key_id: "blood",
apple_key_pem_b64: "orange")
publish_enabled: true)
end

it "can publish via the apple configs" do
Expand Down

0 comments on commit 042957d

Please sign in to comment.