File Coverage

blib/lib/Google/Ads/GoogleAds/OAuth2ServiceAccountsHandler.pm
Criterion Covered Total %
statement 45 64 70.3
branch 3 10 30.0
condition 6 11 54.5
subroutine 12 14 85.7
pod 1 1 100.0
total 67 100 67.0


line stmt bran cond sub pod time code
1             # Copyright 2021, Google LLC
2             #
3             # Licensed under the Apache License, Version 2.0 (the "License");
4             # you may not use this file except in compliance with the License.
5             # You may obtain a copy of the License at
6             #
7             # http://www.apache.org/licenses/LICENSE-2.0
8             #
9             # Unless required by applicable law or agreed to in writing, software
10             # distributed under the License is distributed on an "AS IS" BASIS,
11             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12             # See the License for the specific language governing permissions and
13             # limitations under the License.
14              
15              
16             use strict;
17 11     11   59 use warnings;
  11         21  
  11         263  
18 11     11   44 use version;
  11         17  
  11         216  
19 11     11   47 use base qw(Google::Ads::GoogleAds::Common::OAuth2BaseHandler);
  11         15  
  11         92  
20 11     11   693  
  11         16  
  11         696  
21             # The following needs to be on one line because CPAN uses a particularly hacky
22             # eval() to determine module versions.
23             use Google::Ads::GoogleAds::Constants; our $VERSION = ${Google::Ads::GoogleAds::Constants::VERSION};
24 11     11   81  
  11         26  
  11         491  
25             use Class::Std::Fast;
26 11     11   57 use JSON::XS;
  11         13  
  11         65  
27 11     11   8026 use JSON::WebToken;
  11         32355  
  11         474  
28 11     11   4453  
  11         108358  
  11         1511  
29             # Class::Std-style attributes. Need to be kept in the same line.
30             # These need to go in the same line for older Perl interpreters to understand.
31             my %json_key_file_path_of : ATTR(:name<json_key_file_path> :default<>);
32             my %impersonated_email_of : ATTR(:name<impersonated_email> :default<>);
33             my %additional_scopes_of : ATTR(:name<additional_scopes> :default<>);
34              
35             # Methods from Google::Ads::GoogleAds::Common::AuthHandlerInterface.
36             my ($self, $api_client, $properties) = @_;
37             my $ident = ident $self;
38 9     0 1 1551  
39 9         25 $json_key_file_path_of{$ident} = $properties->{jsonKeyFilePath}
40             || $json_key_file_path_of{$ident};
41             $impersonated_email_of{$ident} = $properties->{impersonatedEmail}
42 9   66     71 || $impersonated_email_of{$ident};
43              
44 9   66     43 # Below attributes are not in the googleads.properties configuration.
45             $additional_scopes_of{$ident} = $properties->{additionalScopes}
46             || $additional_scopes_of{$ident};
47             }
48 9   66     36  
49 11     11   73 # Methods from Google::Ads::GoogleAds::Common::OAuth2BaseHandler.
  11         21  
  11         57  
50             my $self = shift;
51              
52             if (!$self->get_json_key_file_path()) {
53 1     1   2 return 0;
54             }
55 1 50       3  
56 1         6 my $json_key = $self->__read_json_key_file() || return 0;
57             my $time = time;
58              
59 0   0     0 my $jwt = JSON::WebToken->encode({
60 0         0 iss => $json_key->{client_email},
61             scope => $self->__formatted_scopes(),
62             aud => Google::Ads::GoogleAds::Constants::OAUTH2_BASE_URL . "/token",
63             exp => $time + 3600,
64             iat => $time,
65             sub => $self->get_impersonated_email()
66             },
67             $json_key->{private_key},
68             "RS256"
69             );
70              
71 0         0 my $response = $self->get___lwp_agent()->post(
72             Google::Ads::GoogleAds::Constants::OAUTH2_BASE_URL . "/token",
73             {
74 0         0 grant_type => "urn:ietf:params:oauth:grant-type:jwt-bearer",
75             assertion => $jwt
76             });
77              
78             if (!$response->is_success()) {
79             my $err_msg = $response->decoded_content();
80             $self->get_api_client()->get_die_on_faults()
81 0 0       0 ? die($err_msg)
82 0         0 : warn($err_msg);
83 0 0       0 return 0;
84             }
85              
86 0         0 my $content_hash = $self->__parse_auth_response($response->decoded_content());
87              
88             $self->set_access_token($content_hash->{access_token});
89 0         0 $self->set_access_token_expires(time + $content_hash->{expires_in});
90              
91 0         0 return 1;
92 0         0 }
93              
94 0         0 my $self = shift;
95             my @parsed_scopes = ();
96             my $additional_scopes = $self->get_additional_scopes();
97             if ($additional_scopes) {
98 5     5   1942 @parsed_scopes = split(/\s*,\s*/, $additional_scopes);
99 5         7 }
100 5         13 push @parsed_scopes, Google::Ads::GoogleAds::Constants::DEFAULT_OAUTH2_SCOPE;
101 5 100       26 return @parsed_scopes;
102 3         10 }
103              
104 5         7 # Retrieves the OAuth2 scopes defined in _scope as a list separated by commas.
105 5         18 # This is the format expected when sending the OAuth request.
106             my $self = shift;
107             my @parsed_scopes = $self->_scope();
108             return join(',', @parsed_scopes);
109             }
110              
111 2     2   567 # Reads the values from the specified JSON key file path as a JSON object.
112 2         6 my $self = shift;
113 2         11  
114             my $json_text;
115             open(KEY_FILE, $self->get_json_key_file_path()) || return 0;
116             while (<KEY_FILE>) {
117             $json_text .= $_;
118 0     0     }
119             close(KEY_FILE);
120 0            
121 0 0         return decode_json($json_text);
122 0           }
123 0            
124             1;
125 0            
126             =pod
127 0            
128             =head1 NAME
129              
130             Google::Ads::GoogleAds::OAuth2ServiceAccountsHandler
131              
132             =head1 DESCRIPTION
133              
134             A concrete implementation of L<Google::Ads::GoogleAds::Common::OAuth2BaseHandler>
135             that supports OAuth2 for service accounts and defines the scope required to
136             access the Google Ads API server.
137              
138             See L<https://developers.google.com/identity/protocols/oauth2/service-account>
139             for details of the protocol.
140              
141             =head1 ATTRIBUTES
142              
143             Each of these attributes can be set via
144             Google::Ads::GoogleAds::OAuth2ServiceAccountsHandler->new().
145              
146             Alternatively, there is a get_ and set_ method associated with each attribute
147             for retrieving or setting them dynamically.
148              
149             =head2 api_client
150              
151             A reference to the API client used to handle the API requests.
152              
153             =head2 json_key_file_path
154              
155             The absolute path to the local JSON key file for the OAuth2 service account.
156              
157             =head2 impersonated_email
158              
159             The email address account to impersonate, when the service account has been
160             delegated domain wide access.
161              
162             =head2 access_token
163              
164             Stores an OAuth2 access token after the authorization flow is followed or for
165             you to manually set it in case you had it previously stored. If this is manually
166             set this handler will verify its validity before preparing a request.
167              
168             =head2 additional_scopes
169              
170             Stores additional OAuth2 scopes as a comma-separated string.
171             These scopes define which services the tokens are allowed to access,
172             e.g. https://www.googleapis.com/auth/analytics.
173              
174             =head1 METHODS
175              
176             =head2 initialize
177              
178             Initializes the handler with the API client object and the properties such as
179             json_key_file_path and impersonated_email, used for generating authorization
180             requests.
181              
182             =head3 Parameters
183              
184             =over
185              
186             =item *
187              
188             A required I<api_client> with a reference to the API client object handling the
189             requests against the API.
190              
191             =item *
192              
193             A hash reference with the following keys:
194              
195             {
196             jsonKeyFilePath => "json-key-file-path",
197             impersonatedEmail => "impersonated-email",
198             accessToken => "access-token",
199             additionalScopes => "additional-scopes",
200             }
201              
202             Refer to the documentation of the properties as L</json_key_file_path>,
203             L</impersonated_email>, L</access_token> and L</additional_scopes>.
204              
205             =back
206              
207             =head2 prepare_request
208              
209             Refer to L<Google::Ads::GoogleAds::Common::AuthHandlerInterface> documentation
210             of this method.
211              
212             =head2 is_auth_enabled
213              
214             Refer to L<Google::Ads::GoogleAds::Common::AuthHandlerInterface> documentation
215             of this method.
216              
217             =head2 _scope
218              
219             Method defined by L<Google::Ads::GoogleAds::Common::OAuth2BaseHandler> and
220             implemented in this class to return the required OAuth2 scopes as an array.
221              
222             =head2 _refresh_access_token
223              
224             Method defined by L<Google::Ads::GoogleAds::Common::OAuth2BaseHandler> and
225             implemented in this class to refresh the stored OAuth2 access token.
226              
227             =head2 __formatted_scopes
228              
229             Private method to return the OAuth2 scopes as a list of strings separated by
230             commas. This is the format expected when sending the OAuth request.
231              
232             =head3 Returns
233              
234             The string of OAuth2 scopes separated by commas.
235              
236             =head1 LICENSE AND COPYRIGHT
237              
238             Copyright 2021 Google LLC
239              
240             Licensed under the Apache License, Version 2.0 (the "License");
241             you may not use this file except in compliance with the License.
242             You may obtain a copy of the License at
243              
244             http://www.apache.org/licenses/LICENSE-2.0
245              
246             Unless required by applicable law or agreed to in writing, software
247             distributed under the License is distributed on an "AS IS" BASIS,
248             WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
249             See the License for the specific language governing permissions and
250             limitations under the License.
251              
252             =head1 REPOSITORY INFORMATION
253              
254             $Rev: $
255             $LastChangedBy: $
256             $Id: $
257              
258             =cut