Skip to content
This repository has been archived by the owner on Jun 1, 2023. It is now read-only.

Enable Theme Access passwords for theme serve #2681

Merged
merged 4 commits into from
Dec 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ From version 2.6.0, the sections in this file adhere to the [keep a changelog](h
## [Unreleased]

### Added
* [#2681](https://github.com/Shopify/shopify-cli/pull/2681): Enable Theme Access passwords for theme serve
* [#2701](https://github.com/Shopify/shopify-cli/pull/2701): Update theme-check to 1.12.1 (introduce intelligent code completion)

## Version 2.32.1 - 2022-12-05
Expand Down
22 changes: 18 additions & 4 deletions lib/shopify_cli/theme/dev_server/proxy.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# frozen_string_literal: true

require "stringio"
require "time"
require "cgi"
Expand All @@ -20,6 +21,7 @@ class DevServer
"transfer-encoding",
"upgrade",
"content-security-policy",
"content-length",
]

class Proxy
Expand Down Expand Up @@ -54,14 +56,14 @@ def call(env)
"POST", env["PATH_INFO"],
headers: headers,
query: query,
form_data: form_data.merge(replace_templates).merge(_method: env["REQUEST_METHOD"]),
form_data: form_data.merge(replace_templates).merge(_method: env["REQUEST_METHOD"])
)
else
request(
env["REQUEST_METHOD"], env["PATH_INFO"],
headers: headers,
query: query,
body_stream: (env["rack.input"] if has_body?(headers)),
body_stream: (env["rack.input"] if has_body?(headers))
)
end

Expand Down Expand Up @@ -97,7 +99,7 @@ def has_body?(headers)

def bearer_token
Environment.storefront_renderer_auth_token ||
ShopifyCLI::DB.get(:storefront_renderer_production_exchange_token) ||
ShopifyCLI::DB.get(:storefront_renderer_production_exchange_token) ||
raise(KeyError, "storefront_renderer_production_exchange_token missing")
end

Expand Down Expand Up @@ -152,11 +154,13 @@ def add_session_cookie(cookie_header)

def secure_session_id_expired?
return true unless @secure_session_id && @last_session_cookie_refresh

Time.now - @last_session_cookie_refresh >= SESSION_COOKIE_MAX_AGE
end

def extract_secure_session_id_from_response_headers(headers)
return unless headers["set-cookie"]

headers["set-cookie"][SESSION_COOKIE_REGEXP, 1]
end

Expand All @@ -173,7 +177,7 @@ def secure_session_id

def get_response_headers(response, env)
response_headers = normalize_headers(
response.respond_to?(:headers) ? response.headers : response.to_hash
response.respond_to?(:headers) ? response.headers : response.to_hash,
)
# According to https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-7.1.3.1Acc
# should remove hop-by-hop header fields
Expand All @@ -196,6 +200,16 @@ def get_response_headers(response, env)

def request(method, path, headers: nil, query: [], form_data: nil, body_stream: nil)
uri = URI.join("https://#{shop}", path)

if Environment.theme_access_password?
headers = headers ? headers.slice("ACCEPT", "CONTENT-TYPE", "CONTENT-LENGTH", "Cookie") : {}
headers.merge!({
"X-Shopify-Access-Token" => Environment.admin_auth_token,
"X-Shopify-Shop" => shop,
})
uri = URI.join("https://#{ThemeAccessAPI::BASE_URL}", "cli/sfr#{path}")
end

uri.query = URI.encode_www_form(query + [[:_fd, 0], [:pb, 0]])

@ctx.debug("Proxying #{method} #{uri}")
Expand Down
49 changes: 38 additions & 11 deletions test/shopify-cli/theme/dev_server/proxy_test.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# frozen_string_literal: true

require "test_helper"
require "shopify_cli/theme/dev_server/proxy"
require "shopify_cli/theme/development_theme"
Expand Down Expand Up @@ -51,6 +52,32 @@ def test_get_is_proxied_to_online_store
request.get("/")
end

def test_get_is_proxied_to_theme_access_api_when_password_is_provided
Environment.stubs(:theme_access_password?).returns(true)
Environment.stubs(:store).returns("https://dev-theme-server-store.myshopify.com")
stub_request(:head, "https://theme-kit-access.shopifyapps.com/cli/sfr/?_fd=0&pb=0&preview_theme_id=123456789")
.with(
headers: { "X-Shopify-Shop" => "https://dev-theme-server-store.myshopify.com" },
)
.to_return(
status: 200,
headers: { "Set-Cookie" => "_secure_session_id=#{SECURE_SESSION_ID}" },
)
stub_request(:get, "https://theme-kit-access.shopifyapps.com/cli/sfr/?_fd=0&pb=0")
.with(
headers: {
"Content-Length" => "0",
"Cookie" => "_secure_session_id=deadbeef",
"X-Shopify-Shop" => "https://dev-theme-server-store.myshopify.com",
},
)
.to_return(status: 200, body: "", headers: {})

request.get("/")

assert_requested(:get, "https://theme-kit-access.shopifyapps.com/cli/sfr/?_fd=0&pb=0")
end

def test_refreshes_session_cookie_on_expiry
stub_request(:get, "https://dev-theme-server-store.myshopify.com/?_fd=0&pb=0")
.with(
Expand Down Expand Up @@ -82,22 +109,22 @@ def test_update_session_cookie_when_returned_from_backend
.with(
headers: {
"Cookie" => "_secure_session_id=#{SECURE_SESSION_ID}",
}
},
)
.to_return(
status: 200,
body: "",
headers: {
"Set-Cookie" => "_secure_session_id=#{new_secure_session_id}",
}
},
)

# GET / passing the new session cookie
stub_request(:get, "https://dev-theme-server-store.myshopify.com/?_fd=0&pb=0")
.with(
headers: {
"Cookie" => "_secure_session_id=#{new_secure_session_id}",
}
},
)
.to_return(status: 200)

Expand All @@ -114,7 +141,7 @@ def test_form_data_is_proxied_to_online_store
},
headers: default_proxy_headers.merge(
"Content-Type" => "application/x-www-form-urlencoded",
)
),
)
.to_return(status: 200)

Expand All @@ -132,7 +159,7 @@ def test_multipart_is_proxied_to_online_store
headers: default_proxy_headers.merge(
"Content-Length" => "272",
"Content-Type" => "multipart/form-data; boundary=AaB03x",
)
),
)
.to_return(status: 200)

Expand Down Expand Up @@ -221,7 +248,7 @@ def test_replaces_secure_session_id_cookie
.with(
headers: {
"Cookie" => "_secure_session_id=#{SECURE_SESSION_ID}",
}
},
)

stub_session_id_request
Expand All @@ -234,7 +261,7 @@ def test_appends_secure_session_id_cookie
.with(
headers: {
"Cookie" => "cart_currency=CAD; secure_customer_sig=; _secure_session_id=#{SECURE_SESSION_ID}",
}
},
)

stub_session_id_request
Expand Down Expand Up @@ -274,7 +301,7 @@ def test_pass_pending_templates_to_storefront
"Host" => "dev-theme-server-store.myshopify.com",
"X-Forwarded-For" => "",
"User-Agent" => "Shopify CLI",
}
},
)
.to_return(status: 200, body: "PROXY RESPONSE")

Expand All @@ -301,7 +328,7 @@ def test_patching_store_urls
"layout/theme.liquid" => @theme["layout/theme.liquid"].read,
},
},
headers: { "User-Agent" => "Shopify CLI" }
headers: { "User-Agent" => "Shopify CLI" },
)
.to_return(status: 200, body: <<-PROXY_RESPONSE)
<html>
Expand Down Expand Up @@ -428,13 +455,13 @@ def stub_session_id_request
.with(
headers: {
"Host" => "dev-theme-server-store.myshopify.com",
}
},
)
.to_return(
status: 200,
headers: {
"Set-Cookie" => "_secure_session_id=#{SECURE_SESSION_ID}",
}
},
)
end
end
Expand Down