Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@

package com.google.firebase.appdistribution.gradle

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential
import com.google.api.client.http.HttpTransport
import com.google.auth.http.HttpCredentialsAdapter

interface AppDistributionEnvironment {
fun getFirebaseCliLoginCredentials(transport: HttpTransport): GoogleCredential?
fun getFirebaseCliLoginCredentials(): HttpCredentialsAdapter?

companion object {
const val ENV_FIREBASE_TOKEN = "FIREBASE_TOKEN"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@

package com.google.firebase.appdistribution.gradle

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential
import com.google.api.client.http.HttpTransport
import com.google.auth.http.HttpCredentialsAdapter
import com.google.firebase.appdistribution.gradle.models.FirebaseCliConfig
import com.google.gson.FieldNamingPolicy
import com.google.gson.GsonBuilder
Expand All @@ -28,7 +27,7 @@ import java.nio.file.Paths
import org.gradle.api.logging.Logging

class AppDistributionEnvironmentImpl : AppDistributionEnvironment {
override fun getFirebaseCliLoginCredentials(transport: HttpTransport): GoogleCredential? {
override fun getFirebaseCliLoginCredentials(): HttpCredentialsAdapter? {
val gson =
GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create()

Expand All @@ -46,7 +45,7 @@ class AppDistributionEnvironmentImpl : AppDistributionEnvironment {

// Step 3: Generate new credential using refresh token
if (config?.tokens?.refreshToken != null) {
val refreshToken = RefreshToken(config.tokens.refreshToken, transport)
val refreshToken = RefreshToken(config.tokens.refreshToken)
return refreshToken.generateNewCredentials()
}
} catch (e: IOException) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@

package com.google.firebase.appdistribution.gradle

import com.google.api.client.auth.oauth2.Credential
import com.google.api.client.googleapis.GoogleUtils
import com.google.api.client.http.GenericUrl
import com.google.api.client.http.HttpContent
import com.google.api.client.http.HttpRequestFactory
import com.google.api.client.http.HttpRequestInitializer
import com.google.api.client.http.HttpTransport
import com.google.api.client.http.javanet.NetHttpTransport
import java.io.IOException
Expand All @@ -34,8 +34,8 @@ private constructor(
private val httpRequestFactory: HttpRequestFactory,
) {
constructor(
credential: Credential
) : this(newGoogleHttpTransport().createRequestFactory(credential))
requestInitializer: HttpRequestInitializer
) : this(newGoogleHttpTransport().createRequestFactory(requestInitializer))

constructor(httpTransport: HttpTransport) : this(httpTransport.createRequestFactory())

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,32 @@

package com.google.firebase.appdistribution.gradle

import com.google.api.client.auth.oauth2.Credential
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport
import com.google.api.client.http.HttpTransport
import com.google.firebase.appdistribution.gradle.AppDistributionEnvironment.Companion.ENV_FIREBASE_TOKEN
import com.google.firebase.appdistribution.gradle.AppDistributionEnvironment.Companion.ENV_GOOGLE_APPLICATION_CREDENTIALS
import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.REFRESH_TOKEN_ERROR
import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.SERVICE_CREDENTIALS_NOT_FOUND
import com.google.firebase.appdistribution.gradle.OptionsUtils.ensureFileExists
import com.google.firebase.appdistribution.gradle.models.ServiceAccountCredentials
import com.google.auth.http.HttpCredentialsAdapter
import com.google.auth.oauth2.GoogleCredentials
import java.io.IOException
import org.gradle.api.logging.Logging

/** Helper class for retrieving auth credentials from one of the four possible auth mechanisms. */
class CredentialsRetriever(
private val httpTransport: HttpTransport = GoogleNetHttpTransport.newTrustedTransport(),
private val appDistributionEnvironment: AppDistributionEnvironment =
AppDistributionEnvironmentImpl()
AppDistributionEnvironmentImpl(),
private val adcCredentialsProvider: () -> GoogleCredentials = { GoogleCredentials.getApplicationDefault() }
) {

internal constructor(
appDistributionEnvironment: AppDistributionEnvironment
) : this(GoogleNetHttpTransport.newTrustedTransport(), appDistributionEnvironment)

/** Returns the auth credential, if found. Otherwise returns null. */
fun getAuthCredential(serviceCredentialsPath: String? = null): Credential? {
fun getAuthCredential(serviceCredentialsPath: String? = null): HttpCredentialsAdapter? {
// Check the explicitly passed in service credentials first
if (serviceCredentialsPath != null) {
val serviceCredentialsFile =
ensureFileExists(serviceCredentialsPath, SERVICE_CREDENTIALS_NOT_FOUND)
return try {
ServiceAccountCredentials.fromFile(serviceCredentialsFile).googleCredential
ServiceAccountCredentials.fromFile(serviceCredentialsFile).credentialsAdapter
} catch (e: IOException) {
throw AppDistributionException(
SERVICE_CREDENTIALS_NOT_FOUND,
Expand All @@ -64,7 +59,7 @@ class CredentialsRetriever(
"Using credentials token specified by environment variable {}",
ENV_FIREBASE_TOKEN
)
RefreshToken(envRefreshToken, httpTransport).generateNewCredentials()
RefreshToken(envRefreshToken).generateNewCredentials()
} catch (e: Exception) {
throw AppDistributionException(
REFRESH_TOKEN_ERROR,
Expand All @@ -75,8 +70,7 @@ class CredentialsRetriever(
}

// Then check for cached Firebase CLI tokens
val firebaseCliLoginCreds =
appDistributionEnvironment.getFirebaseCliLoginCredentials(httpTransport)
val firebaseCliLoginCreds = appDistributionEnvironment.getFirebaseCliLoginCredentials()
if (firebaseCliLoginCreds != null) {
logger.info("Using cached Firebase CLI credentials")
return firebaseCliLoginCreds
Expand All @@ -95,7 +89,7 @@ class CredentialsRetriever(
return try {
val envServiceAccountCredentials =
ServiceAccountCredentials.fromFile(serviceCredentialsFile)
envServiceAccountCredentials.googleCredential
envServiceAccountCredentials.credentialsAdapter
} catch (e: IOException) {
throw AppDistributionException(
SERVICE_CREDENTIALS_NOT_FOUND,
Expand All @@ -105,6 +99,15 @@ class CredentialsRetriever(
}
}

// Lastly, check for standard Application Default Credentials (ADC) as a fallback
try {
val credentials = adcCredentialsProvider().createScoped(ApiEndpoints.SCOPES)
logger.info("Using Application Default Credentials (ADC)")
return HttpCredentialsAdapter(credentials)
} catch (e: IOException) {
logger.debug("Failed to load Application Default Credentials (ADC)", e)
}

// If we reach this point, we were unable to find valid credentials
return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package com.google.firebase.appdistribution.gradle

import com.google.api.client.auth.oauth2.Credential
import com.google.auth.http.HttpCredentialsAdapter
import com.google.api.client.http.HttpResponseException
import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.MISSING_CREDENTIALS
import java.io.IOException
Expand Down Expand Up @@ -76,9 +76,11 @@ object FirebaseAppDistribution {
private fun getCredential(options: TesterManagementOptions) =
options.credential ?: throw AppDistributionException(MISSING_CREDENTIALS)

private fun getAuthenticatedHttpClient(credential: Credential): AuthenticatedHttpClient {
private fun getAuthenticatedHttpClient(
credentialsAdapter: HttpCredentialsAdapter
): AuthenticatedHttpClient {
return try {
AuthenticatedHttpClient(credential)
AuthenticatedHttpClient(credentialsAdapter)
} catch (e: Exception) {
when (e) {
is GeneralSecurityException,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,50 +16,38 @@

package com.google.firebase.appdistribution.gradle

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential
import com.google.api.client.googleapis.auth.oauth2.GoogleRefreshTokenRequest
import com.google.api.client.http.HttpTransport
import com.google.api.client.json.gson.GsonFactory
import java.io.IOException
import com.google.auth.http.HttpCredentialsAdapter
import com.google.auth.oauth2.UserCredentials

/**
* This class encapsulates a refresh token that can be used for generate a new [GoogleCredential] to
* be used when authenticating requests. By using the refresh token the is returned after the oauth
* flow, we can generate new access tokens for future requests.
* This class encapsulates a refresh token that can be used for generate a new
* [HttpCredentialsAdapter] to be used when authenticating requests. By using the refresh token that
* is returned after the oauth flow, we can generate new access tokens for future requests.
*/
class RefreshToken(private val refreshToken: String, private val transport: HttpTransport) {
class RefreshToken(private val refreshToken: String) {
/**
* Using the provided refresh token, this makes a request to generate a new access token that can
* be used to authenticate requests
*
* @return the credentials that were generated from the refresh token. Its worth noting that these
* credentials do not include the refresh token, only a new access token
* @throws IOException thrown if there was a problem making a request to the oauth provider
* @return the credentials that were generated from the refresh token.
*/
fun generateNewCredentials(): GoogleCredential {
val response =
GoogleRefreshTokenRequest(
transport,
GsonFactory.getDefaultInstance(),
refreshToken,
CLIENT_ID,
CLIENT_SECRET
)
.execute()

val credential = GoogleCredential.Builder().build()
credential.setFromTokenResponse(response)

return credential
fun generateNewCredentials(): HttpCredentialsAdapter {
return HttpCredentialsAdapter(
UserCredentials.newBuilder()
.setClientId(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
.setRefreshToken(refreshToken)
.build()
)
}

companion object {
// Firebase CLI ClientID and ClientSecret
// In this type of application, the client secret is not treated as a secret.
// See: https://developers.google.com/identity/protocols/OAuth2InstalledApp
// TODO: Use separate ID's/secrets for each tool
private const val CLIENT_ID =
internal const val CLIENT_ID =
"563584335869-fgrhgmd47bqnekij5i8b5pr03ho849e6.apps.googleusercontent.com"
private const val CLIENT_SECRET = "j9iVZfS8kkCEFUPaAeJV0sAi"
internal const val CLIENT_SECRET = "j9iVZfS8kkCEFUPaAeJV0sAi"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
package com.google.firebase.appdistribution.gradle

import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport
import com.google.auth.http.HttpCredentialsAdapter
import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.MISSING_PROJECT_NUMBER
import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.MISSING_TESTER_EMAILS
import com.google.firebase.appdistribution.gradle.OptionsUtils.getValueFromStringOrFile
Expand All @@ -27,14 +27,11 @@ class TesterManagementOptions(
emailsValue: String? = null,
emailsFile: String? = null,
serviceCredentialsFile: String? = null,
credentialsRetriever: CredentialsRetriever =
CredentialsRetriever(
GoogleNetHttpTransport.newTrustedTransport(),
AppDistributionEnvironmentImpl()
)
credentialsRetriever: CredentialsRetriever = CredentialsRetriever()
) {
val emails = splitCommaOrNewlineSeparatedString(getValueFromStringOrFile(emailsValue, emailsFile))
val credential = credentialsRetriever.getAuthCredential(serviceCredentialsFile)
val credential: HttpCredentialsAdapter? =
credentialsRetriever.getAuthCredential(serviceCredentialsFile)

init {
// If project number is set it would be greater than 0, the default primitive value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@

package com.google.firebase.appdistribution.gradle

import com.google.api.client.auth.oauth2.Credential
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport
import com.google.api.client.http.HttpTransport
import com.google.auth.http.HttpCredentialsAdapter
import com.google.common.base.Splitter
import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.Companion.binaryNotFoundError
import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.INVALID_APP_ID
Expand Down Expand Up @@ -47,7 +45,6 @@ internal constructor(
val debug: Boolean = false,
val testNonBlocking: Boolean = false,
binaryPath: String,
transport: HttpTransport = GoogleNetHttpTransport.newTrustedTransport(),
appDistributionEnvironment: AppDistributionEnvironment = AppDistributionEnvironmentImpl(),
serviceCredentialsFile: String? = null,
releaseNotesValue: String? = null,
Expand All @@ -71,8 +68,8 @@ internal constructor(
val testers = extractValues(testersValue, testersPath)
val groups = extractValues(groupsValue, groupsPath)
val testDevices = extractTestDevices(testDevicesValue, testDevicesPath)
val credential: Credential? =
CredentialsRetriever(transport, appDistributionEnvironment)
val credential: HttpCredentialsAdapter? =
CredentialsRetriever(appDistributionEnvironment)
.getAuthCredential(serviceCredentialsFile)
val testCases = extractValues(testCasesValue, testCasesPath)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,21 @@

package com.google.firebase.appdistribution.gradle.models

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential
import com.google.auth.http.HttpCredentialsAdapter
import com.google.auth.oauth2.GoogleCredentials
import com.google.firebase.appdistribution.gradle.ApiEndpoints
import java.io.File
import java.nio.file.Files

class ServiceAccountCredentials private constructor(val googleCredential: GoogleCredential) {
class ServiceAccountCredentials
private constructor(val credentialsAdapter: HttpCredentialsAdapter) {
companion object {
fun fromFile(credentials: File): ServiceAccountCredentials =
ServiceAccountCredentials(
GoogleCredential.fromStream(Files.newInputStream(credentials.toPath()))
.createScoped(ApiEndpoints.SCOPES)
HttpCredentialsAdapter(
GoogleCredentials.fromStream(Files.newInputStream(credentials.toPath()))
.createScoped(ApiEndpoints.SCOPES)
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "external_account",
"audience": "//iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/my-pool/providers/my-provider",
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
"token_url": "https://sts.googleapis.com/v1/token",
"credential_source": {
"file": "/var/run/secrets/token"
}
}
Loading
Loading