fastlane-plugin-firebase_app_distribution 0.3.2 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_action.rb +6 -2
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_login.rb +63 -12
- data/lib/fastlane/plugin/firebase_app_distribution/client/firebase_app_distribution_api_client.rb +1 -1
- data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_auth_client.rb +34 -8
- data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_error_message.rb +2 -2
- data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_helper.rb +4 -5
- data/lib/fastlane/plugin/firebase_app_distribution/version.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95f39c2a13ac05b24c98fe9a6d1fc86b0b943c2863ef1828389456e6d6755760
|
4
|
+
data.tar.gz: b2bcc6f542c283d7bb2b8627dd09d4c1bcf2efb5831b5383a94a062cdc508df6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c6cdc3695ceb9c9eb3cc5615e7f6a3cd5ce7a51abfb7f08ef782f82148b506b77993f58ce2b66543d43b0b529959f2bea2a52d3c57db165b4fa4a8bcc7c2db7
|
7
|
+
data.tar.gz: 00d23c9c049c7a1058d07e5f2df2eb05eb7a41ee8c7cf15aca07e3e524a99752f679d238eb99054367ebbb86ba95cc3391be007bb23f95817119345511f6e451
|
data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_action.rb
CHANGED
@@ -18,6 +18,10 @@ module Fastlane
|
|
18
18
|
def self.run(params)
|
19
19
|
params.values # to validate all inputs before looking for the ipa/apk/aab
|
20
20
|
|
21
|
+
if params[:debug]
|
22
|
+
UI.important("Warning: Debug logging enabled. Output may include sensitive information.")
|
23
|
+
end
|
24
|
+
|
21
25
|
app_id = app_id_from_params(params)
|
22
26
|
app_name = app_name_from_app_id(app_id)
|
23
27
|
platform = lane_platform || platform_from_app_id(app_id)
|
@@ -197,12 +201,12 @@ module Fastlane
|
|
197
201
|
type: String),
|
198
202
|
FastlaneCore::ConfigItem.new(key: :groups,
|
199
203
|
env_name: "FIREBASEAPPDISTRO_GROUPS",
|
200
|
-
description: "The
|
204
|
+
description: "The group aliases used for distribution, separated by commas",
|
201
205
|
optional: true,
|
202
206
|
type: String),
|
203
207
|
FastlaneCore::ConfigItem.new(key: :groups_file,
|
204
208
|
env_name: "FIREBASEAPPDISTRO_GROUPS_FILE",
|
205
|
-
description: "The
|
209
|
+
description: "The group aliases used for distribution, separated by commas",
|
206
210
|
optional: true,
|
207
211
|
type: String),
|
208
212
|
FastlaneCore::ConfigItem.new(key: :testers,
|
data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_login.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
require 'googleauth'
|
2
|
-
require 'googleauth/stores/file_token_store'
|
3
2
|
require "fileutils"
|
4
3
|
|
5
4
|
module Fastlane
|
6
5
|
module Actions
|
7
6
|
class FirebaseAppDistributionLoginAction < Action
|
8
|
-
OOB_URI = "urn:ietf:wg:oauth:2.0:oob"
|
9
7
|
SCOPE = "https://www.googleapis.com/auth/cloud-platform"
|
10
8
|
|
11
9
|
# In this type of application, the client secret is not treated as a secret.
|
@@ -14,24 +12,65 @@ module Fastlane
|
|
14
12
|
CLIENT_SECRET = "j9iVZfS8kkCEFUPaAeJV0sAi"
|
15
13
|
|
16
14
|
def self.run(params)
|
15
|
+
callback_uri = "http://localhost:#{params[:port]}"
|
17
16
|
client_id = Google::Auth::ClientId.new(CLIENT_ID, CLIENT_SECRET)
|
18
|
-
authorizer = Google::Auth::UserAuthorizer.new(client_id, SCOPE, nil)
|
19
|
-
|
17
|
+
authorizer = Google::Auth::UserAuthorizer.new(client_id, SCOPE, nil, callback_uri)
|
18
|
+
|
19
|
+
# Create an anti-forgery state token as described here:
|
20
|
+
# https://developers.google.com/identity/protocols/OpenIDConnect#createxsrftoken
|
21
|
+
state = SecureRandom.hex(16)
|
22
|
+
url = authorizer.get_authorization_url(state: state)
|
20
23
|
|
21
24
|
UI.message("Open the following address in your browser and sign in with your Google account:")
|
22
25
|
UI.message(url)
|
23
|
-
UI.message("")
|
24
|
-
code = UI.input("Enter the resulting code here: ")
|
25
|
-
credentials = authorizer.get_credentials_from_code(code: code, base_url: OOB_URI)
|
26
|
-
UI.message("")
|
27
26
|
|
27
|
+
response_params = get_authorization_code(params[:port])
|
28
|
+
|
29
|
+
# Confirm that the state in the response matches the state token used to
|
30
|
+
# generate the authorization URL.
|
31
|
+
unless state == response_params['state'][0]
|
32
|
+
UI.crash!('An error has occurred. The state parameter in the authorization response does not match the expected state, which could mean that a malicious attacker is trying to make a login request.')
|
33
|
+
end
|
34
|
+
|
35
|
+
user_credentials = authorizer.get_credentials_from_code(
|
36
|
+
code: response_params['code'][0]
|
37
|
+
)
|
28
38
|
UI.success("Set the refresh token as the FIREBASE_TOKEN environment variable")
|
29
|
-
UI.success("Refresh Token: #{
|
30
|
-
rescue Signet::AuthorizationError
|
31
|
-
UI.error("The code you entered is invalid. Copy and paste the code and try again.")
|
39
|
+
UI.success("Refresh Token: #{user_credentials.refresh_token}")
|
32
40
|
rescue => error
|
33
41
|
UI.error(error.to_s)
|
34
|
-
UI.crash!("An error has
|
42
|
+
UI.crash!("An error has occurred, please login again.")
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.get_authorization_code(port)
|
46
|
+
begin
|
47
|
+
server = TCPServer.open(port)
|
48
|
+
rescue Errno::EADDRINUSE => error
|
49
|
+
UI.error(error.to_s)
|
50
|
+
UI.crash!("Port #{port} is in use. Please specify a different one using the port parameter.")
|
51
|
+
end
|
52
|
+
client = server.accept
|
53
|
+
callback_request = client.readline
|
54
|
+
# Use a regular expression to extract the request line from the first line of
|
55
|
+
# the callback request, e.g.:
|
56
|
+
# GET /?code=AUTH_CODE&state=XYZ&scope=... HTTP/1.1
|
57
|
+
matcher = /GET +([^ ]+)/.match(callback_request)
|
58
|
+
response_params = CGI.parse(URI.parse(matcher[1]).query) unless matcher.nil?
|
59
|
+
|
60
|
+
client.puts("HTTP/1.1 200 OK")
|
61
|
+
client.puts("Content-Type: text/html")
|
62
|
+
client.puts("")
|
63
|
+
client.puts("<b>")
|
64
|
+
if response_params['code'].nil?
|
65
|
+
client.puts("Failed to retrieve authorization code.")
|
66
|
+
else
|
67
|
+
client.puts("Authorization code was successfully retrieved.")
|
68
|
+
end
|
69
|
+
client.puts("</b>")
|
70
|
+
client.puts("<p>Please check the console output.</p>")
|
71
|
+
client.close
|
72
|
+
|
73
|
+
return response_params
|
35
74
|
end
|
36
75
|
|
37
76
|
#####################################################
|
@@ -55,6 +94,18 @@ module Fastlane
|
|
55
94
|
def self.is_supported?(platform)
|
56
95
|
[:ios, :android].include?(platform)
|
57
96
|
end
|
97
|
+
|
98
|
+
def self.available_options
|
99
|
+
[
|
100
|
+
FastlaneCore::ConfigItem.new(key: :port,
|
101
|
+
env_name: "FIREBASEAPPDISTRO_LOGIN_PORT",
|
102
|
+
description: "Port for the local web server which receives the response from Google's authorization server",
|
103
|
+
default_value: "8081",
|
104
|
+
optional: true,
|
105
|
+
type: String)
|
106
|
+
|
107
|
+
]
|
108
|
+
end
|
58
109
|
end
|
59
110
|
end
|
60
111
|
end
|
data/lib/fastlane/plugin/firebase_app_distribution/client/firebase_app_distribution_api_client.rb
CHANGED
@@ -46,7 +46,7 @@ module Fastlane
|
|
46
46
|
request.headers[CONTENT_TYPE] = APPLICATION_JSON
|
47
47
|
end
|
48
48
|
rescue Faraday::ClientError
|
49
|
-
UI.user_error!("#{ErrorMessage::INVALID_TESTERS} \nEmails: #{emails} \
|
49
|
+
UI.user_error!("#{ErrorMessage::INVALID_TESTERS} \nEmails: #{emails} \nGroup Aliases: #{group_aliases}")
|
50
50
|
end
|
51
51
|
UI.success("✅ Added testers/groups.")
|
52
52
|
end
|
data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_auth_client.rb
CHANGED
@@ -4,6 +4,8 @@ module Fastlane
|
|
4
4
|
module Auth
|
5
5
|
module FirebaseAppDistributionAuthClient
|
6
6
|
TOKEN_CREDENTIAL_URI = "https://oauth2.googleapis.com/token"
|
7
|
+
REDACTION_EXPOSED_LENGTH = 5
|
8
|
+
REDACTION_CHARACTER = "X"
|
7
9
|
|
8
10
|
# Returns the auth token for any of the auth methods (Firebase CLI token,
|
9
11
|
# Google service account, firebase-tools). To ensure that a specific
|
@@ -73,8 +75,14 @@ module Fastlane
|
|
73
75
|
client.fetch_access_token!
|
74
76
|
client.access_token
|
75
77
|
rescue Signet::AuthorizationError => error
|
76
|
-
|
77
|
-
|
78
|
+
error_message = ErrorMessage::REFRESH_TOKEN_ERROR
|
79
|
+
if debug
|
80
|
+
error_message += "\nRefresh token used: #{format_token(refresh_token)}\n"
|
81
|
+
error_message += error_details(error)
|
82
|
+
else
|
83
|
+
error_message += " #{debug_instructions}"
|
84
|
+
end
|
85
|
+
UI.user_error!(error_message)
|
78
86
|
end
|
79
87
|
|
80
88
|
def service_account(google_service_path, debug)
|
@@ -86,14 +94,32 @@ module Fastlane
|
|
86
94
|
rescue Errno::ENOENT
|
87
95
|
UI.user_error!("#{ErrorMessage::SERVICE_CREDENTIALS_NOT_FOUND}: #{google_service_path}")
|
88
96
|
rescue Signet::AuthorizationError => error
|
89
|
-
|
90
|
-
|
97
|
+
error_message = "#{ErrorMessage::SERVICE_CREDENTIALS_ERROR}: \"#{google_service_path}\""
|
98
|
+
if debug
|
99
|
+
error_message += "\n#{error_details(error)}"
|
100
|
+
else
|
101
|
+
error_message += ". #{debug_instructions}"
|
102
|
+
end
|
103
|
+
UI.user_error!(error_message)
|
104
|
+
end
|
105
|
+
|
106
|
+
def error_details(error)
|
107
|
+
"#{error.message}\nResponse status: #{error.response.status}"
|
108
|
+
end
|
109
|
+
|
110
|
+
def debug_instructions
|
111
|
+
"For more information, try again with firebase_app_distribution's \"debug\" parameter set to \"true\"."
|
91
112
|
end
|
92
113
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
114
|
+
# Formats and redacts a token for printing out during debug logging. Examples:
|
115
|
+
# 'abcd' -> '"abcd"''
|
116
|
+
# 'abcdef1234' -> '"XXXXXf1234" (redacted)'
|
117
|
+
def format_token(str)
|
118
|
+
redaction_notice = str.length > REDACTION_EXPOSED_LENGTH ? " (redacted)" : ""
|
119
|
+
exposed_start_char = [str.length - REDACTION_EXPOSED_LENGTH, 0].max
|
120
|
+
exposed_characters = str[exposed_start_char, REDACTION_EXPOSED_LENGTH]
|
121
|
+
redacted_characters = REDACTION_CHARACTER * [str.length - REDACTION_EXPOSED_LENGTH, 0].max
|
122
|
+
"\"#{redacted_characters}#{exposed_characters}\"#{redaction_notice}"
|
97
123
|
end
|
98
124
|
end
|
99
125
|
end
|
data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_error_message.rb
CHANGED
@@ -11,9 +11,9 @@ module ErrorMessage
|
|
11
11
|
INVALID_APP_ID = "App Distribution could not find your app. Make sure to onboard your app by pressing the \"Get started\" button on the App Distribution page in the Firebase console: https://console.firebase.google.com/project/_/appdistribution. App ID"
|
12
12
|
INVALID_PROJECT = "App Distribution could not find your Firebase project. Make sure to onboard an app in your project by pressing the \"Get started\" button on the App Distribution page in the Firebase console: https://console.firebase.google.com/project/_/appdistribution."
|
13
13
|
INVALID_PATH = "Could not read content from"
|
14
|
-
INVALID_TESTERS = "Could not enable access for testers. Check that the groups exist and
|
14
|
+
INVALID_TESTERS = "Could not enable access for testers. Check that the tester emails are formatted correctly, the groups exist and you are using group aliases (not group names) for specifying groups."
|
15
15
|
INVALID_RELEASE_NOTES = "Failed to add release notes"
|
16
|
-
SERVICE_CREDENTIALS_ERROR = "App Distribution could not generate credentials from the service credentials file specified
|
16
|
+
SERVICE_CREDENTIALS_ERROR = "App Distribution could not generate credentials from the service credentials file specified"
|
17
17
|
PLAY_ACCOUNT_NOT_LINKED = "This project is not linked to a Google Play account."
|
18
18
|
APP_NOT_PUBLISHED = "This app is not published in the Google Play console."
|
19
19
|
NO_APP_WITH_GIVEN_BUNDLE_ID_IN_PLAY_ACCOUNT = "App with matching package name does not exist in Google Play."
|
data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_helper.rb
CHANGED
@@ -24,13 +24,12 @@ module Fastlane
|
|
24
24
|
value
|
25
25
|
end
|
26
26
|
|
27
|
-
# Returns the array representation of a string with comma
|
28
|
-
#
|
29
|
-
# Does not work with strings whose individual values have spaces. EX "Hello World" the space will be removed to "HelloWorld"
|
27
|
+
# Returns the array representation of a string with trimmed comma
|
28
|
+
# seperated values.
|
30
29
|
def string_to_array(string)
|
31
30
|
return nil if string.nil? || string.empty?
|
32
|
-
|
33
|
-
|
31
|
+
# Strip string and then strip individual values
|
32
|
+
string.strip.split(",").map(&:strip)
|
34
33
|
end
|
35
34
|
|
36
35
|
def parse_plist(path)
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-firebase_app_distribution
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefan Natchev
|
8
8
|
- Manny Jimenez
|
9
9
|
- Alonso Salas Infante
|
10
|
-
autorequire:
|
10
|
+
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2022-04-14 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: pry
|
@@ -138,7 +138,7 @@ dependencies:
|
|
138
138
|
- - ">="
|
139
139
|
- !ruby/object:Gem::Version
|
140
140
|
version: 2.127.1
|
141
|
-
description:
|
141
|
+
description:
|
142
142
|
email:
|
143
143
|
- snatchev@google.com
|
144
144
|
- mannyjimenez@google.com
|
@@ -168,7 +168,7 @@ homepage: https://github.com/fastlane/fastlane-plugin-firebase_app_distribution
|
|
168
168
|
licenses:
|
169
169
|
- MIT
|
170
170
|
metadata: {}
|
171
|
-
post_install_message:
|
171
|
+
post_install_message:
|
172
172
|
rdoc_options: []
|
173
173
|
require_paths:
|
174
174
|
- lib
|
@@ -183,8 +183,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
183
183
|
- !ruby/object:Gem::Version
|
184
184
|
version: '0'
|
185
185
|
requirements: []
|
186
|
-
rubygems_version: 3.
|
187
|
-
signing_key:
|
186
|
+
rubygems_version: 3.1.4
|
187
|
+
signing_key:
|
188
188
|
specification_version: 4
|
189
189
|
summary: Release your beta builds to Firebase App Distribution. https://firebase.google.com/docs/app-distribution
|
190
190
|
test_files: []
|