@@ -25,6 +25,9 @@ use crate::controller::AlterError;
2525/// endpoint without requiring any specific IAM grants on the service account.
2626const VALIDATION_SCOPE : & str = "https://www.googleapis.com/auth/cloud-platform" ;
2727
28+ /// Modern GCP Service Account keys always use the same token URI.
29+ const OAUTH_TOKEN_URI : & str = "https://oauth2.googleapis.com/token" ;
30+
2831/// GCP connection configuration.
2932#[ derive( Arbitrary , Clone , Debug , Eq , PartialEq , Serialize , Deserialize , Hash ) ]
3033pub struct GcpConnection {
@@ -40,6 +43,28 @@ impl AlterCompatible for GcpConnection {
4043 }
4144}
4245
46+ /// `gcp_auth` parses the Service Account Key JSON into a private type,
47+ /// so we have to write our own deserializer to check whether it's safe.
48+ #[ derive( Deserialize ) ]
49+ pub struct GcpServiceAccountKeyTokenUri {
50+ token_uri : String ,
51+ }
52+
53+ impl GcpServiceAccountKeyTokenUri {
54+ pub fn validate_json ( json : & str ) -> Result < ( ) , anyhow:: Error > {
55+ let k: GcpServiceAccountKeyTokenUri =
56+ serde_json:: from_str ( json) . map_err ( anyhow:: Error :: from) ?;
57+
58+ if k. token_uri != OAUTH_TOKEN_URI {
59+ return Err ( anyhow:: Error :: msg ( format ! (
60+ "token_uri must be {OAUTH_TOKEN_URI}."
61+ ) ) ) ;
62+ }
63+
64+ Ok ( ( ) )
65+ }
66+ }
67+
4368impl GcpConnection {
4469 /// Validates this connection by reading the service-account key out of the
4570 /// secrets store, parsing it, and exchanging it for an OAuth2 access token
@@ -55,8 +80,10 @@ impl GcpConnection {
5580 . read_string ( self . credentials_json )
5681 . await
5782 . map_err ( GcpConnectionValidationError :: SecretRead ) ?;
58- let service_account = CustomServiceAccount :: from_json ( & json)
83+ GcpServiceAccountKeyTokenUri :: validate_json ( & json)
5984 . map_err ( GcpConnectionValidationError :: ParseKey ) ?;
85+ let service_account = CustomServiceAccount :: from_json ( & json)
86+ . map_err ( |e| GcpConnectionValidationError :: ParseKey ( anyhow:: Error :: from ( e) ) ) ?;
6087 service_account
6188 . token ( & [ VALIDATION_SCOPE ] )
6289 . await
@@ -75,7 +102,7 @@ pub enum GcpConnectionValidationError {
75102 #[ error( "failed to read service-account key from secret store: {}" , . 0 . display_with_causes( ) ) ]
76103 SecretRead ( #[ source] anyhow:: Error ) ,
77104 #[ error( "failed to parse service-account key JSON: {}" , . 0 . display_with_causes( ) ) ]
78- ParseKey ( #[ source] gcp_auth :: Error ) ,
105+ ParseKey ( #[ source] anyhow :: Error ) ,
79106 #[ error( "failed to obtain access token from Google: {}" , . 0 . display_with_causes( ) ) ]
80107 FetchToken ( #[ source] gcp_auth:: Error ) ,
81108}
0 commit comments