def main(): try: Utils.GetAuthToken(login_email, password, AUTH_TOKEN_SERVICE, LIB_SIG, None) except AuthTokenError, e: if str(e).find('InvalidSecondFactor') != -1: print """The user has enabled two factor authentication in this account. Have the user generate an application-specific password to make calls against the AdWords API. See http://adwordsapi.blogspot.com/2011/02/authentication-changes-with-2-step.html for more details.""" else: raise e
def _SetHeaders(self): """Sets the SOAP headers for this service's requests.""" now = time.time() if ((('authToken' not in self._headers and 'auth_token_epoch' not in self._config) or int(now - self._config['auth_token_epoch']) >= AUTH_TOKEN_EXPIRE) and not self._headers.get('oauth2credentials')): if ('email' not in self._headers or not self._headers['email'] or 'password' not in self._headers or not self._headers['password']): raise ValidationError( 'Required authentication headers, \'email\' and ' '\'password\', are missing. Unable to regenerate ' 'authentication token.') self._headers['authToken'] = Utils.GetAuthToken( self._headers['email'], self._headers['password'], AUTH_TOKEN_SERVICE, LIB_SIG, self._config['proxy']) self._config['auth_token_epoch'] = time.time() # Apply headers to the SOAPpy service. soap_headers = SOAPpy.Types.headerType( attrs={'xmlns': self._namespace}) request_header_data = {} if 'authToken' in self._headers: authentication_block = SOAPpy.Types.structType( data={'token': self._headers['authToken']}, name='authentication', typed=0, attrs={(SOAPpy.NS.XSI3, 'type'): 'ClientLogin'}) request_header_data['authentication'] = authentication_block for key in self._headers: if (key in GenericDfpService._IGNORED_HEADER_VALUES or not self._headers[key]): continue value = self._headers[key] if key == 'applicationName': value = ''.join([value, LIB_SIG]) request_header_data[key] = SOAPpy.Types.stringType(value) request_header = SOAPpy.Types.structType(data=request_header_data, name='RequestHeader', typed=0) soap_headers.RequestHeader = request_header if 'authToken' in self._headers: soap_headers.RequestHeader._keyord = [ 'applicationName', 'authentication' ] self._soappyservice.soapproxy.header = soap_headers
def _SetHeaders(self): """Sets the SOAP headers for this service's requests.""" now = time.time() if ((('authToken' not in self._headers and 'auth_token_epoch' not in self._config) or int(now - self._config['auth_token_epoch']) >= AUTH_TOKEN_EXPIRE) and not self._headers.get('oauth2credentials')): if ('email' not in self._headers or not self._headers['email'] or 'password' not in self._headers or not self._headers['password']): raise ValidationError( 'Required authentication headers, \'email\' and ' '\'password\', are missing. Unable to regenerate ' 'authentication token.') self._headers['authToken'] = Utils.GetAuthToken( self._headers['email'], self._headers['password'], AUTH_TOKEN_SERVICE, LIB_SIG, self._config['proxy']) self._config['auth_token_epoch'] = time.time() # Apply headers to the SOAPpy service. header_attrs = { 'xmlns': self._namespace, 'xmlns:cm': ('https://adwords.google.com/api/adwords/cm/' + self._op_config['version']) } soap_headers = SOAPpy.Types.headerType(attrs=header_attrs) request_header_data = {} for key in GenericAdWordsService._POSSIBLE_ADWORDS_REQUEST_HEADERS: if (key in GenericAdWordsService._OAUTH_IGNORE_HEADERS and self._headers.get('oauth2credentials')): continue if key in self._headers and self._headers[key]: value = self._headers[key] if key in GenericAdWordsService._STR_CONVERT: value = str(value) elif key == 'userAgent': value = ''.join([value, LIB_SIG]) request_header_data['cm:' + key] = SOAPpy.Types.stringType(value) request_header = SOAPpy.Types.structType(data=request_header_data, name='RequestHeader', typed=0) soap_headers.RequestHeader = request_header self._soappyservice.soapproxy.header = soap_headers
def __ReloadAuthToken(self): """Ensures we have a valid auth_token in our headers.""" # Load/set authentication token. If authentication token has expired, # regenerate it. now = time.time() if (('authToken' not in self.__service._headers and 'auth_token_epoch' not in self._config) or int(now - self._config['auth_token_epoch']) >= AUTH_TOKEN_EXPIRE): if ('email' not in self.__service._headers or not self._headers['email'] or 'password' not in self.__service._headers or not self.__service._headers['password']): msg = ('Required authentication headers, \'email\' and \'password\', ' 'are missing. Unable to regenerate authentication token.') raise ValidationError(msg) self._headers['authToken'] = Utils.GetAuthToken( self.__service._headers['email'], self.__service._headers['password'], AUTH_TOKEN_SERVICE, LIB_SIG, self._config['proxy']) self._config['auth_token_epoch'] = time.time()
def __init__(self, headers=None, config=None, path=None): """Inits Client. Args: [optional] headers: dict Object with populated authentication credentials. config: dict Object with client configuration values. path: str Relative or absolute path to home directory (i.e. location of pickles and logs/). Example: headers = { 'email': '*****@*****.**', 'password': '******', 'authToken': '...', 'applicationName': 'GoogleTest', 'networkCode': 'ca-01234567', 'oauth2credentials': 'See use_oauth2.py' } config = { 'home': '/path/to/home', 'log_home': '/path/to/logs/home', 'proxy': 'http://example.com:8080', 'xml_parser': '1', # PYXML = 1, ELEMENTREE = 2 'debug': 'n', 'raw_debug': 'n', 'xml_log': 'y', 'request_log': 'y', 'raw_response': 'n', 'strict': 'y', 'pretty_xml': 'y', 'compress': 'y', 'access': '' } path = '/path/to/home' """ super(DfpClient, self).__init__(headers, config, path) self.__lock = thread.allocate_lock() self.__loc = None if path is not None: # Update absolute path for a given instance of DfpClient, based on # provided relative path. if os.path.isabs(path): DfpClient.home = path else: # NOTE(api.sgrinberg): Keep first parameter of join() as os.getcwd(), # do not change it to DfpClient.home. Otherwise, may break when # multiple instances of DfpClient exist during program run. DfpClient.home = os.path.join(os.getcwd(), path) # If pickles don't exist at given location, default to "~". if (not headers and not config and (not os.path.exists(os.path.join(DfpClient.home, DfpClient.auth_pkl_name)) or not os.path.exists(os.path.join(DfpClient.home, DfpClient.config_pkl_name)))): DfpClient.home = os.path.expanduser('~') elif not headers: DfpClient.home = os.path.expanduser('~') # Update location for both pickles. DfpClient.auth_pkl = os.path.join(DfpClient.home, DfpClient.auth_pkl_name) DfpClient.config_pkl = os.path.join(DfpClient.home, DfpClient.config_pkl_name) # Only load from the pickle if config wasn't specified. self._config = config or self.__LoadConfigValues() self._config = self.__SetMissingDefaultConfigValues(self._config) self._config['home'] = DfpClient.home # Validate XML parser to use. SanityCheck.ValidateConfigXmlParser(self._config['xml_parser']) # Only load from the pickle if 'headers' wasn't specified. if headers is None: self._headers = self.__LoadAuthCredentials() else: if Utils.BoolTypeConvert(self._config['strict']): SanityCheck.ValidateRequiredHeaders(headers, REQUIRED_SOAP_HEADERS) self._headers = headers # Load/set authentication token. try: if headers and 'authToken' in headers and headers['authToken']: self._headers['authToken'] = headers['authToken'] elif 'email' in self._headers and 'password' in self._headers: self._headers['authToken'] = Utils.GetAuthToken( self._headers['email'], self._headers['password'], AUTH_TOKEN_SERVICE, LIB_SIG, self._config['proxy']) elif (self._headers.get('oauth2credentials')): # If they have oauth2credentials, that's also fine. pass else: msg = ('Authentication data, email or/and password, OAuth2 credentials ' 'is missing.') raise ValidationError(msg) self._config['auth_token_epoch'] = time.time() except AuthTokenError: # We would end up here if non-valid Google Account's credentials were # specified. self._headers['authToken'] = None self._config['auth_token_epoch'] = 0 # Initialize logger. self.__logger = Logger(LIB_SIG, self._config['log_home'])
def CallMethod(self, method_name, params, service_name=None, loc=None, request=None): """Make an API call to specified method. Args: method_name: str API method name. params: list List of parameters to send to the API method. [optional] service_name: str API service name. loc: service Locator. request: instance Holder of the SOAP request. Returns: tuple/str Response from the API method. If 'raw_response' flag enabled a string is returned, tuple otherwise. """ # Acquire thread lock. self._lock.acquire() try: if not service_name and self.__service: service_name = self.__service headers = self._headers config = self._config config['data_injects'] = () error = {} # Load/unload version specific authentication and configuration data. if AdWordsSanityCheck.IsJaxbApi(self._op_config['version']): # Set boolean to the format expected by the server, True => true. for key in ['validateOnly', 'partialFailure']: if key in self._headers: self._headers[key] = self._headers[key].lower() # Load/set authentication token. If authentication token has expired, # regenerate it. now = time.time() if ((('authToken' not in headers and 'auth_token_epoch' not in config) or int(now - config['auth_token_epoch']) >= AUTH_TOKEN_EXPIRE) and not 'oauth_enabled' in config): if ('email' not in headers or not headers['email'] or 'password' not in headers or not headers['password']): msg = ( 'Required authentication headers, \'email\' and ' '\'password\', are missing. Unable to regenerate ' 'authentication token.') raise ValidationError(msg) headers['authToken'] = Utils.GetAuthToken( headers['email'], headers['password'], AUTH_TOKEN_SERVICE, LIB_SIG, config['proxy']) config['auth_token_epoch'] = time.time() self._headers = headers self._config = config headers = Utils.UnLoadDictKeys( headers, ['email', 'password', 'useragent']) ns = '/'.join([ 'https://adwords.google.com/api/adwords', self._op_config['group'], self._op_config['version'] ]) default_ns = '/'.join([ 'https://adwords.google.com/api/adwords', self._op_config['default_group'], self._op_config['version'] ]) config['ns_target'] = (ns, 'RequestHeader') if (config['soap_lib'] == SOAPPY and (self._op_config['default_group'] != self._op_config['group'] or self.__service == 'BulkMutateJobService')): from adspygoogle.adwords.soappy import SERVICE_TYPES data_injects = [] for header in headers: if headers[header]: data_injects.append((header, 'ns1:%s' % header)) data_injects.append( ('<RequestHeader>', '<RequestHeader xmlns="%s" xmlns:ns1="%s">' % (ns, default_ns))) data_injects.append( ('<SOAP-ENV:Body xmlns="%s">' % ns, '<SOAP-ENV:Body xmlns="%s" xmlns:ns1="%s">' % (ns, default_ns))) for item in SERVICE_TYPES: if (item['group'] == self._op_config['default_group'] and re.compile( '<%s>|<%s ' % (item['attr'], item['attr'])).findall(params)): # TODO(api.sgrinberg): Find a more elegant solution that doesn't # involve manual triggering for attributes that show up in both # groups. if ((self._op_config['group'] == 'o' and item['attr'] == 'urls') or (self.__service == 'TrafficEstimatorService' and item['attr'] == 'maxCpc') or item['attr'] == 'selector' or item['attr'] == 'operator'): continue if self.__service != 'BulkMutateJobService': data_injects.append( (' xsi3:type="%s">' % item['type'], '>')) data_injects.append(('<%s>' % item['attr'], '<ns1:%s>' % item['attr'])) data_injects.append(('<%s ' % item['attr'], '<ns1:%s ' % item['attr'])) data_injects.append(('</%s>' % item['attr'], '</ns1:%s>' % item['attr'])) config['data_injects'] = tuple(data_injects) else: headers['useragent'] = headers['userAgent'] headers = Utils.UnLoadDictKeys(headers, ['authToken', 'userAgent']) config = Utils.UnLoadDictKeys( config, ['ns_target', 'auth_token_epoch']) buf = AdWordsSoapBuffer(xml_parser=self._config['xml_parser'], pretty_xml=Utils.BoolTypeConvert( self._config['pretty_xml'])) start_time = time.strftime('%Y-%m-%d %H:%M:%S') response = super(AdWordsWebService, self).CallMethod( headers, config, method_name, params, buf, AdWordsSanityCheck.IsJaxbApi(self._op_config['version']), LIB_SIG, LIB_URL, service_name, loc, request) stop_time = time.strftime('%Y-%m-%d %H:%M:%S') # Restore list type which was overwritten by SOAPpy. if config['soap_lib'] == SOAPPY and isinstance(response, tuple): from adspygoogle.common.soappy import MessageHandler holder = [] for element in response: holder.append( MessageHandler.RestoreListType( element, ('value', 'partialFailureErrors', 'conversionTypes'))) response = tuple(holder) if isinstance(response, dict) or isinstance(response, Error): error = response if not Utils.BoolTypeConvert(self.__config['raw_debug']): self.__ManageSoap(buf, start_time, stop_time, error) finally: # Release thread lock. if self._lock.locked(): self._lock.release() if Utils.BoolTypeConvert(self._config['raw_response']): return response return response
def CallMethod(self, method_name, params, service_name=None, loc=None, request=None): """Make an API call to specified method. Args: method_name: str API method name. params: list List of parameters to send to the API method. [optional] service_name: str API service name. loc: service Locator. request: instance Holder of the SOAP request. Returns: tuple/str Response from the API method. If 'raw_response' flag enabled a string is returned, tuple otherwise. """ # Acquire thread lock. self._lock.acquire() try: headers = self._headers config = self._config config['data_injects'] = () error = {} # Load/set authentication token. If authentication token has expired, # regenerate it. now = time.time() if ((('authToken' not in headers and 'auth_token_epoch' not in config) or int(now - config['auth_token_epoch']) >= AUTH_TOKEN_EXPIRE)): headers['authToken'] = Utils.GetAuthToken( headers['email'], headers['password'], AUTH_TOKEN_SERVICE, LIB_SIG, config['proxy']) config['auth_token_epoch'] = time.time() self._headers = headers self._config = config headers = Utils.UnLoadDictKeys(Utils.CleanUpDict(headers), ['email', 'password']) name_space = '/'.join([ 'https://www.google.com/apis/ads/publisher', self._op_config['version'] ]) config['ns_target'] = (name_space, 'RequestHeader') # Load new authentication headers, starting with version v201103. data_injects = [] if self.__op_config['version'] > 'v201101': new_headers = {} for key in headers: if key == 'authToken' and headers[key]: if config['soap_lib'] == SOAPPY: data_injects.append( ('<authentication>', '<authentication xsi3:type="ClientLogin">')) config['data_injects'] = tuple(data_injects) else: config['auth_type'] = 'ClientLogin' new_headers['authentication'] = { 'token': headers['authToken'] } elif key == 'oAuthToken' and headers[key]: # TODO(api.sgrinberg): Add support for OAuth. pass else: new_headers[key] = headers[key] headers = new_headers buf = DfpSoapBuffer(xml_parser=self._config['xml_parser'], pretty_xml=Utils.BoolTypeConvert( self._config['pretty_xml'])) start_time = time.strftime('%Y-%m-%d %H:%M:%S') response = super(DfpWebService, self).CallMethod( headers, config, method_name, params, buf, DfpSanityCheck.IsJaxbApi(self._op_config['version']), LIB_SIG, LIB_URL, service_name, loc, request) stop_time = time.strftime('%Y-%m-%d %H:%M:%S') # Restore list type which was overwritten by SOAPpy. if config['soap_lib'] == SOAPPY and isinstance(response, tuple): from adspygoogle.common.soappy import MessageHandler holder = [] for element in response: holder.append( MessageHandler.RestoreListType( element, ('results', 'afcFormats', 'sizes', 'targetedAdUnitIds', 'excludedAdUnitIds', 'targetedPlacementIds', 'frequencyCaps', 'creativeSizes'))) response = tuple(holder) if isinstance(response, dict) or isinstance(response, Error): error = response if not Utils.BoolTypeConvert(self.__config['raw_debug']): self.__ManageSoap(buf, start_time, stop_time, error) finally: # Release thread lock. if self._lock.locked(): self._lock.release() if Utils.BoolTypeConvert(self._config['raw_response']): return response return response