Skip to content

Commit 7b3533d

Browse files
author
Rachel Prince
committed
Add headers to support Android-application restricted API keys
1 parent e445aca commit 7b3533d

File tree

4 files changed

+72
-17
lines changed

4 files changed

+72
-17
lines changed

firebase-app-distribution/src/main/java/com/google/firebase/appdistribution/CheckForNewReleaseClient.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ AppDistributionReleaseInternal getNewReleaseFromClient(
125125
throws FirebaseAppDistributionException {
126126
try {
127127
AppDistributionReleaseInternal retrievedNewRelease =
128-
firebaseAppDistributionTesterApiClient.fetchNewRelease(fid, appId, apiKey, authToken);
128+
firebaseAppDistributionTesterApiClient.fetchNewRelease(
129+
fid, appId, apiKey, authToken, firebaseApp.getApplicationContext());
129130

130131
if (isNewerBuildVersion(retrievedNewRelease) || !isInstalledRelease(retrievedNewRelease)) {
131132
return retrievedNewRelease;

firebase-app-distribution/src/main/java/com/google/firebase/appdistribution/FirebaseAppDistributionTesterApiClient.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,16 @@
1414

1515
package com.google.firebase.appdistribution;
1616

17+
import static android.content.ContentValues.TAG;
1718
import static com.google.firebase.appdistribution.FirebaseAppDistributionException.Status.AUTHENTICATION_FAILURE;
1819
import static com.google.firebase.appdistribution.FirebaseAppDistributionException.Status.NETWORK_FAILURE;
1920

21+
import android.content.Context;
22+
import android.content.pm.PackageManager;
23+
import android.util.Log;
2024
import androidx.annotation.NonNull;
25+
import com.google.android.gms.common.util.AndroidUtilsLight;
26+
import com.google.android.gms.common.util.Hex;
2127
import com.google.firebase.appdistribution.internal.AppDistributionReleaseInternal;
2228
import java.io.BufferedInputStream;
2329
import java.io.ByteArrayOutputStream;
@@ -36,18 +42,27 @@ class FirebaseAppDistributionTesterApiClient {
3642
private static final String REQUEST_METHOD = "GET";
3743
private static final String API_KEY_HEADER = "x-goog-api-key";
3844
private static final String INSTALLATION_AUTH_HEADER = "X-Goog-Firebase-Installations-Auth";
45+
private static final String X_ANDROID_PACKAGE_HEADER_KEY = "X-Android-Package";
46+
private static final String X_ANDROID_CERT_HEADER_KEY = "X-Android-Cert";
47+
3948
private static final String BUILD_VERSION_JSON_KEY = "buildVersion";
4049
private static final String DISPLAY_VERSION_JSON_KEY = "displayVersion";
4150
private static final String RELEASE_NOTES_JSON_KEY = "releaseNotes";
4251
private static final String BINARY_TYPE_JSON_KEY = "binaryType";
4352
private static final String CODE_HASH_KEY = "codeHash";
4453
private static final String IAS_ARTIFACT_ID_KEY = "iasArtifactId";
4554
private static final String DOWNLOAD_URL_KEY = "downloadUrl";
55+
4656
private static final String TAG = "TesterApiClient:";
57+
4758
public static final int DEFAULT_BUFFER_SIZE = 8192;
4859

4960
public @NonNull AppDistributionReleaseInternal fetchNewRelease(
50-
@NonNull String fid, @NonNull String appId, @NonNull String apiKey, @NonNull String authToken)
61+
@NonNull String fid,
62+
@NonNull String appId,
63+
@NonNull String apiKey,
64+
@NonNull String authToken,
65+
@NonNull Context context)
5166
throws FirebaseAppDistributionException {
5267

5368
AppDistributionReleaseInternal newRelease;
@@ -56,6 +71,9 @@ class FirebaseAppDistributionTesterApiClient {
5671
connection.setRequestMethod(REQUEST_METHOD);
5772
connection.setRequestProperty(API_KEY_HEADER, apiKey);
5873
connection.setRequestProperty(INSTALLATION_AUTH_HEADER, authToken);
74+
connection.addRequestProperty(X_ANDROID_PACKAGE_HEADER_KEY, context.getPackageName());
75+
connection.addRequestProperty(
76+
X_ANDROID_CERT_HEADER_KEY, getFingerprintHashForPackage(context));
5977

6078
InputStream inputStream = connection.getInputStream();
6179
JSONObject newReleaseJson = readFetchReleaseInputStream(inputStream);
@@ -185,4 +203,27 @@ private static String convertInputStreamToString(InputStream is) throws IOExcept
185203
}
186204
return result.toString();
187205
}
206+
207+
/**
208+
* Gets the Android package's SHA-1 fingerprint.
209+
*
210+
* @param context
211+
*/
212+
private String getFingerprintHashForPackage(Context context) {
213+
byte[] hash;
214+
215+
try {
216+
hash = AndroidUtilsLight.getPackageCertificateHashBytes(context, context.getPackageName());
217+
218+
if (hash == null) {
219+
Log.e(TAG, "Could not get fingerprint hash for package: " + context.getPackageName());
220+
return null;
221+
} else {
222+
return Hex.bytesToStringUppercase(hash, /* zeroTerminated= */ false);
223+
}
224+
} catch (PackageManager.NameNotFoundException e) {
225+
Log.e(TAG, "No such package: " + context.getPackageName(), e);
226+
return null;
227+
}
228+
}
188229
}

firebase-app-distribution/src/test/java/com/google/firebase/appdistribution/CheckForNewReleaseClientTest.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import static org.mockito.Mockito.when;
3030
import static org.robolectric.Shadows.shadowOf;
3131

32+
import android.content.Context;
3233
import android.content.pm.ApplicationInfo;
3334
import android.content.pm.PackageInfo;
3435
import android.os.Bundle;
@@ -87,6 +88,7 @@ public class CheckForNewReleaseClientTest {
8788

8889
private CheckForNewReleaseClient checkForNewReleaseClient;
8990
private ShadowPackageManager shadowPackageManager;
91+
private Context applicationContext;
9092

9193
@Mock private FirebaseInstallationsApi mockFirebaseInstallations;
9294
@Mock private FirebaseAppDistributionTesterApiClient mockFirebaseAppDistributionTesterApiClient;
@@ -117,6 +119,7 @@ public void setup() {
117119

118120
shadowPackageManager =
119121
shadowOf(ApplicationProvider.getApplicationContext().getPackageManager());
122+
applicationContext = ApplicationProvider.getApplicationContext();
120123

121124
ApplicationInfo applicationInfo =
122125
ApplicationInfoBuilder.newBuilder()
@@ -161,7 +164,8 @@ public void checkForNewReleaseTask_whenCalledMultipleTimes_returnsTheSameTask()
161164

162165
@Test
163166
public void checkForNewRelease_succeeds() throws Exception {
164-
when(mockFirebaseAppDistributionTesterApiClient.fetchNewRelease(any(), any(), any(), any()))
167+
when(mockFirebaseAppDistributionTesterApiClient.fetchNewRelease(
168+
any(), any(), any(), any(), any()))
165169
.thenReturn(TEST_RELEASE_NEWER_APK);
166170
when(mockFirebaseInstallations.getId()).thenReturn(Tasks.forResult(TEST_FID_1));
167171
when(mockFirebaseInstallations.getToken(false))
@@ -180,7 +184,8 @@ public void checkForNewRelease_succeeds() throws Exception {
180184

181185
@Test
182186
public void checkForNewRelease_nonAppDistroFailure() throws Exception {
183-
when(mockFirebaseAppDistributionTesterApiClient.fetchNewRelease(any(), any(), any(), any()))
187+
when(mockFirebaseAppDistributionTesterApiClient.fetchNewRelease(
188+
any(), any(), any(), any(), any()))
184189
.thenReturn(TEST_RELEASE_CURRENT);
185190
Exception expectedException = new Exception("test ex");
186191
when(mockFirebaseInstallations.getId()).thenReturn(Tasks.forException(expectedException));
@@ -210,7 +215,8 @@ public void checkForNewRelease_appDistroFailure() throws Exception {
210215
FirebaseAppDistributionException expectedException =
211216
new FirebaseAppDistributionException(
212217
"test", FirebaseAppDistributionException.Status.UNKNOWN);
213-
when(mockFirebaseAppDistributionTesterApiClient.fetchNewRelease(any(), any(), any(), any()))
218+
when(mockFirebaseAppDistributionTesterApiClient.fetchNewRelease(
219+
any(), any(), any(), any(), any()))
214220
.thenThrow(expectedException);
215221

216222
TestOnCompleteListener<AppDistributionReleaseInternal> onCompleteListener =
@@ -228,7 +234,7 @@ public void checkForNewRelease_appDistroFailure() throws Exception {
228234
public void getNewReleaseFromClient_whenNewReleaseIsNewerBuildThanInstalled_returnsRelease()
229235
throws Exception {
230236
when(mockFirebaseAppDistributionTesterApiClient.fetchNewRelease(
231-
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN))
237+
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN, applicationContext))
232238
.thenReturn(TEST_RELEASE_NEWER_APK);
233239

234240
AppDistributionReleaseInternal release =
@@ -242,7 +248,7 @@ public void getNewReleaseFromClient_whenNewReleaseIsNewerBuildThanInstalled_retu
242248
@Test
243249
public void getNewReleaseFromClient_whenNewReleaseIsSameRelease_returnsNull() throws Exception {
244250
when(mockFirebaseAppDistributionTesterApiClient.fetchNewRelease(
245-
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN))
251+
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN, applicationContext))
246252
.thenReturn(TEST_RELEASE_CURRENT);
247253

248254
doReturn(TEST_CODEHASH_2).when(checkForNewReleaseClient).extractApkCodeHash(any());
@@ -256,7 +262,8 @@ public void getNewReleaseFromClient_whenNewReleaseIsSameRelease_returnsNull() th
256262

257263
@Test
258264
public void handleNewReleaseFromClient_whenNewAabIsAvailable_returnsRelease() throws Exception {
259-
when(mockFirebaseAppDistributionTesterApiClient.fetchNewRelease(any(), any(), any(), any()))
265+
when(mockFirebaseAppDistributionTesterApiClient.fetchNewRelease(
266+
any(), any(), any(), any(), any()))
260267
.thenReturn(
261268
AppDistributionReleaseInternal.builder()
262269
.setBuildVersion(TEST_RELEASE_CURRENT.getBuildVersion())
@@ -285,7 +292,8 @@ public void handleNewReleaseFromClient_whenNewAabIsAvailable_returnsRelease() th
285292
@Test
286293
public void handleNewReleaseFromClient_whenNewReleaseIsSameAsInstalledAab_returnsNull()
287294
throws Exception {
288-
when(mockFirebaseAppDistributionTesterApiClient.fetchNewRelease(any(), any(), any(), any()))
295+
when(mockFirebaseAppDistributionTesterApiClient.fetchNewRelease(
296+
any(), any(), any(), any(), any()))
289297
.thenReturn(
290298
AppDistributionReleaseInternal.builder()
291299
.setBuildVersion(TEST_RELEASE_CURRENT.getBuildVersion())

firebase-app-distribution/src/test/java/com/google/firebase/appdistribution/FirebaseAppDistributionTesterApiClientTest.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import static org.junit.Assert.assertThrows;
2020
import static org.mockito.Mockito.when;
2121

22+
import android.content.Context;
23+
import androidx.test.core.app.ApplicationProvider;
2224
import com.google.firebase.appdistribution.internal.AppDistributionReleaseInternal;
2325
import java.io.ByteArrayInputStream;
2426
import java.io.IOException;
@@ -45,6 +47,7 @@ public class FirebaseAppDistributionTesterApiClientTest {
4547
private static final String INVALID_RESPONSE = "InvalidResponse";
4648

4749
private FirebaseAppDistributionTesterApiClient firebaseAppDistributionTesterApiClient;
50+
private Context applicationContext;
4851
@Mock private HttpsURLConnection mockHttpsURLConnection;
4952

5053
@Before
@@ -59,6 +62,8 @@ public void setup() throws Exception {
5962
Mockito.doReturn(mockHttpsURLConnection)
6063
.when(firebaseAppDistributionTesterApiClient)
6164
.openHttpsUrlConnection(TEST_APP_ID_1, TEST_FID_1);
65+
66+
applicationContext = ApplicationProvider.getApplicationContext();
6267
}
6368

6469
@Test
@@ -69,7 +74,7 @@ public void fetchNewRelease_whenResponseSuccessfulForApk_returnsRelease() throws
6974
when(mockHttpsURLConnection.getInputStream()).thenReturn(response);
7075
AppDistributionReleaseInternal release =
7176
firebaseAppDistributionTesterApiClient.fetchNewRelease(
72-
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN);
77+
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN, applicationContext);
7378
assertEquals(release.getBinaryType(), BinaryType.APK);
7479
assertEquals(release.getBuildVersion(), "3");
7580
assertEquals(release.getDisplayVersion(), "3.0");
@@ -86,7 +91,7 @@ public void fetchNewRelease_whenResponseSuccessfulForAab_returnsRelease() throws
8691
when(mockHttpsURLConnection.getInputStream()).thenReturn(response);
8792
AppDistributionReleaseInternal release =
8893
firebaseAppDistributionTesterApiClient.fetchNewRelease(
89-
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN);
94+
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN, applicationContext);
9095
assertEquals(release.getBinaryType(), BinaryType.AAB);
9196
assertEquals(release.getBuildVersion(), "3");
9297
assertEquals(release.getDisplayVersion(), "3.0");
@@ -105,7 +110,7 @@ public void fetchNewRelease_whenResponseFailsWith401_throwsError() throws Except
105110
FirebaseAppDistributionException.class,
106111
() ->
107112
firebaseAppDistributionTesterApiClient.fetchNewRelease(
108-
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN));
113+
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN, applicationContext));
109114

110115
assertEquals(FirebaseAppDistributionException.Status.AUTHENTICATION_FAILURE, ex.getErrorCode());
111116
assertEquals("Failed to authenticate the tester", ex.getMessage());
@@ -121,7 +126,7 @@ public void fetchNewRelease_whenResponseFailsWith403_throwsError() throws Except
121126
FirebaseAppDistributionException.class,
122127
() ->
123128
firebaseAppDistributionTesterApiClient.fetchNewRelease(
124-
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN));
129+
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN, applicationContext));
125130

126131
assertEquals(FirebaseAppDistributionException.Status.AUTHENTICATION_FAILURE, ex.getErrorCode());
127132
assertEquals("Failed to authorize the tester", ex.getMessage());
@@ -137,7 +142,7 @@ public void fetchNewRelease_whenResponseFailsWith404_throwsError() throws Except
137142
FirebaseAppDistributionException.class,
138143
() ->
139144
firebaseAppDistributionTesterApiClient.fetchNewRelease(
140-
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN));
145+
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN, applicationContext));
141146

142147
assertEquals(FirebaseAppDistributionException.Status.AUTHENTICATION_FAILURE, ex.getErrorCode());
143148
assertEquals("Tester or release not found", ex.getMessage());
@@ -153,7 +158,7 @@ public void fetchNewRelease_whenResponseFailsWith504_throwsError() throws Except
153158
FirebaseAppDistributionException.class,
154159
() ->
155160
firebaseAppDistributionTesterApiClient.fetchNewRelease(
156-
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN));
161+
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN, applicationContext));
157162

158163
assertEquals(FirebaseAppDistributionException.Status.NETWORK_FAILURE, ex.getErrorCode());
159164
assertEquals("Failed to fetch releases due to timeout", ex.getMessage());
@@ -169,7 +174,7 @@ public void fetchNewRelease_whenResponseFailsWithUnknownCode_throwsError() throw
169174
FirebaseAppDistributionException.class,
170175
() ->
171176
firebaseAppDistributionTesterApiClient.fetchNewRelease(
172-
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN));
177+
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN, applicationContext));
173178

174179
assertEquals(FirebaseAppDistributionException.Status.NETWORK_FAILURE, ex.getErrorCode());
175180
assertEquals("Failed to fetch releases due to unknown network error", ex.getMessage());
@@ -185,7 +190,7 @@ public void fetchNewRelease_whenInvalidJson_throwsError() throws Exception {
185190
FirebaseAppDistributionException.class,
186191
() ->
187192
firebaseAppDistributionTesterApiClient.fetchNewRelease(
188-
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN));
193+
TEST_FID_1, TEST_APP_ID_1, TEST_API_KEY, TEST_AUTH_TOKEN, applicationContext));
189194

190195
assertEquals(FirebaseAppDistributionException.Status.UNKNOWN, ex.getErrorCode());
191196
assertEquals("Error parsing service response", ex.getMessage());

0 commit comments

Comments
 (0)