Skip to content

Commit

Permalink
Remove use of deprecated GoogleCredential class
Browse files Browse the repository at this point in the history
This change is only related to the Google-Group-checking code in
`play-googleauth`, which was using a deprecated class:

The `com.google.api.client.googleapis.auth.oauth2.GoogleCredential`
class in https://github.com/googleapis/google-api-java-client was
deprecated in May 2019 by googleapis/google-api-java-client#1258
...the deprecation advice recommends using the classes from the new
library: https://github.com/googleapis/google-auth-library-java

This commit switches to using the new
`com.google.auth.oauth2.ServiceAccountCredentials` class from the
`google-auth-library-oauth2-http` artifact. Given this new class, our
custom `com.gu.googleauth.GoogleServiceAccount` Scala case class is no
longer necessary, so has been deprecated, in favour of the user passing
us an instance of `ServiceAccountCredentials` - which is actually
easier for a user to generate! Here's an example of how you make and use
an instance of the new credentials:

```
import org.apache.commons.io.Charsets.UTF_8
import org.apache.commons.io.IOUtils
import com.google.auth.oauth2.ServiceAccountCredentials
import com.gu.googleauth.GoogleGroupChecker

val impersonatedUser: String = ... // email address of the 'impersonated' account
val serviceAccountCert: String = ... // from Google Developers Console
val credentials  =
  ServiceAccountCredentials.fromStream(IOUtils.toInputStream(serviceAccountCert, UTF_8))

val groupChecker = new GoogleGroupChecker(impersonatedUser, credentials)
```
  • Loading branch information
rtyley committed Nov 28, 2020
1 parent 4589b93 commit bc4883d
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 21 deletions.
8 changes: 4 additions & 4 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ val sonatypeReleaseSettings = Seq(

def projectWithPlayVersion(majorMinorVersion: String) =
Project(s"play-v$majorMinorVersion", file(s"play-v$majorMinorVersion")).settings(
scalaVersion := "2.12.10",

scalaVersion := "2.12.12",
crossScalaVersions := Seq(scalaVersion.value, "2.13.4"),
scalacOptions ++= Seq("-feature", "-deprecation"),

libraryDependencies ++= Seq(
Expand All @@ -65,8 +65,8 @@ def projectWithPlayVersion(majorMinorVersion: String) =
sonatypeReleaseSettings
)

lazy val `play-v27` = projectWithPlayVersion("27").settings(crossScalaVersions := Seq(scalaVersion.value, "2.13.1"))
lazy val `play-v28` = projectWithPlayVersion("28").settings(crossScalaVersions := Seq(scalaVersion.value, "2.13.1"))
lazy val `play-v27` = projectWithPlayVersion("27")
lazy val `play-v28` = projectWithPlayVersion("28")

lazy val `play-googleauth-root` = (project in file(".")).aggregate(
`play-v27`,
Expand Down
53 changes: 37 additions & 16 deletions play-v27/src/main/scala/com/gu/googleauth/groups.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package com.gu.googleauth

import java.security.PrivateKey

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport
import com.google.api.client.json.jackson2.JacksonFactory
import com.google.api.services.directory.{Directory, DirectoryScopes}
import com.google.auth.http.HttpCredentialsAdapter
import com.google.auth.oauth2.ServiceAccountCredentials

import scala.collection.JavaConverters._
import scala.concurrent._
Expand All @@ -22,6 +23,7 @@ import scala.concurrent._
* @param privateKey the Service Account's private key - from the P12 file generated when the Service Account was created
* @param impersonatedUser the email address of the user the application will be impersonating
*/
@deprecated("Use com.google.auth.oauth2.ServiceAccountCredentials instead", "play-googleauth 2.1.0")
case class GoogleServiceAccount(
email: String,
privateKey: PrivateKey,
Expand All @@ -35,25 +37,44 @@ case class GoogleServiceAccount(
* doesn't seem to work?). The Service Account needs the following scope:
* https://www.googleapis.com/auth/admin.directory.group.readonly
*
* You also need a separate domain user account (eg [email protected]), which
* will be 'impersonated' when making the calls.
* So long as you have the Service Account certificate as a string, you can easily make
* an instance of com.google.auth.oauth2.ServiceAccountCredentials like this:
*
* {{{
* import org.apache.commons.io.Charsets.UTF_8
* import org.apache.commons.io.IOUtils
* import com.google.auth.oauth2.ServiceAccountCredentials
*
* val serviceAccountCert: String = ... // certificate from Google Developers Console
* val credentials = ServiceAccountCredentials.fromStream(IOUtils.toInputStream(serviceAccountCert, UTF_8))
* }}}
*
* @param impersonatedUser a separate domain-user account email address (eg '[email protected]'), the email address
* of the user the application will be impersonating when making calls.
*/
class GoogleGroupChecker(directoryServiceAccount: GoogleServiceAccount) {
class GoogleGroupChecker(impersonatedUser: String, serviceAccountCredentials: ServiceAccountCredentials) {

val directoryService = {
@deprecated(
"this constructor is deprecated, use the constructor accepting com.google.auth.oauth2.ServiceAccountCredentials instead",
"play-googleauth 2.1.0"
)
def this(googleServiceAccount: GoogleServiceAccount) = {
this(
googleServiceAccount.impersonatedUser,
ServiceAccountCredentials.newBuilder()
.setPrivateKey(googleServiceAccount.privateKey)
.setServiceAccountUser(googleServiceAccount.email)
.build()
)
}

val directoryService: Directory = {
val credentials = serviceAccountCredentials
.createDelegated(impersonatedUser)
.createScoped(DirectoryScopes.ADMIN_DIRECTORY_GROUP_READONLY)
val transport = GoogleNetHttpTransport.newTrustedTransport()
val jsonFactory = JacksonFactory.getDefaultInstance

val credential = new GoogleCredential.Builder()
.setTransport(transport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(directoryServiceAccount.email)
.setServiceAccountUser(directoryServiceAccount.impersonatedUser)
.setServiceAccountPrivateKey(directoryServiceAccount.privateKey)
.setServiceAccountScopes(Seq(DirectoryScopes.ADMIN_DIRECTORY_GROUP_READONLY).asJava)
.build()

new Directory.Builder(transport, jsonFactory, null).setHttpRequestInitializer(credential).build
new Directory.Builder(transport, jsonFactory, new HttpCredentialsAdapter(credentials)).build
}

def retrieveGroupsFor(userEmail: String)(implicit ec: ExecutionContext): Future[Set[String]] = for {
Expand Down
1 change: 1 addition & 0 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ object Dependencies {
val googleDirectoryAPI = Seq(
"com.google.apis" % "google-api-services-admin-directory" % "directory_v1-rev20191003-1.30.8" exclude("com.google.guava", "guava-jdk5"),
"com.google.api-client" % "google-api-client" % "1.31.1", // https://app.snyk.io/vuln/SNYK-JAVA-COMGOOGLEOAUTHCLIENT-575276
"com.google.auth" % "google-auth-library-oauth2-http" % "0.22.0",
"com.google.guava" % "guava" % "30.0-jre"
)

Expand Down
2 changes: 1 addition & 1 deletion version.sbt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version in ThisBuild := "2.0.1-SNAPSHOT"
version in ThisBuild := "2.1.0-SNAPSHOT"

0 comments on commit bc4883d

Please sign in to comment.