def identify(self, environ): """This method is called when a request is incoming. If credentials are found, the returned identity mapping will contain an arbitrary set of key/value pairs. Return None to indicate that the plugin found no appropriate credentials. An IIdentifier plugin is also permitted to ``pre-authenticate'' an identity. If the identifier plugin knows that the identity is ``good'' (e.g. in the case of ticket-based authentication where the user id is embedded into the ticket), it can insert a special key into the identity dictionary: repoze.who.userid. If this key is present in the identity dictionary, no authenticators will be asked to authenticate the identity. """ if not self._has_already_logged_fb_version: environ[REPOZE_WHO_LOGGER].info(u'Using Facebook API v%s', FACEBOOK_API_VERSION_STR) self._has_already_logged_fb_version = True request = Request(environ) response = Response() # First test for logout as we then don't need the rest. if request.path in self.logout_handler_paths: return self._logout(environ, request, response) # --> None # Then we check that we are actually on the URL which is # supposed to be the url to return to (login_handler_path in # configuration) this URL is used for both: the answer for the # login form and when the openid provider redirects the user # back. elif request.path not in self.login_handler_paths: return None try: request.scheme = request.headers['X-Forwarded-Proto'] except KeyError: pass login_handler_url = self._get_full_login_handler_url(request) environ[REPOZE_WHO_LOGGER].debug(u'login_handler_url: %r', login_handler_url) default_target_url = self._deduct_default_target_url(request) if 'access_token' in request.params and 'uid' in request.params: fb_user = { 'access_token': request.params['access_token'], 'uid': request.params['uid'], } data_source = 'from params' elif 'code' in request.params: try: fb_user = facebook.get_access_token_from_code( request.params['code'], login_handler_url, config['pyfacebook.appid'], config['pyfacebook.secret']) except facebook.GraphAPIError as e: self._log_graph_api_exception( 'Exception in get_access_token_from_code()', e, environ) self._redirect(environ, target_url=default_target_url, response=response) return None data_source = 'via Facebook "code"' else: try: fb_user = facebook.get_user_from_cookie( request.cookies, config['pyfacebook.appid'], config['pyfacebook.secret']) except facebook.GraphAPIError as e: self._log_graph_api_exception( 'Exception in get_user_from_cookie()', e, environ) # Redirect to Facebook to get a code for a new access token. self._redirect_to_perms_dialog(environ, login_handler_url) return None data_source = 'from cookie' environ[REPOZE_WHO_LOGGER] \ .info('Received fb_user = %r (%s)', fb_user, data_source) # Store a local instance of the user data so we don't need # a round-trip to Facebook on every request try: graph = facebook.GraphAPI(fb_user["access_token"], version=FACEBOOK_API_VERSION_STR) profile = graph.get_object('me') if 'id' not in profile: environ[REPOZE_WHO_LOGGER] \ .warn('Facebook Python-SDK received no uid.') return self._logout(environ, request, response) # --> None if 'uid' in fb_user: assert profile['id'] == fb_user['uid'] else: fb_user['uid'] = profile['id'] permissions = graph.get_object('me/permissions')['data'] environ[REPOZE_WHO_LOGGER].info(u'Granted Facebook permissions: %r', permissions) if FACEBOOK_API_VERSION >= (2, 0): granted = [ item['permission'] for item in permissions if item['status'] == 'granted' ] missing_mandatory = self.mandatory_permissions - set(granted) missing_optional = self.optional_permissions - set(granted) if missing_optional: environ[REPOZE_WHO_LOGGER]\ .info(u'Missing optional permissions: %r', missing_optional) environ[FACEBOOK_CONNECT_REPOZE_WHO_MISSING_OPTIONAL] = \ missing_optional if missing_mandatory: environ[REPOZE_WHO_LOGGER]\ .info(u'Missing mandatory permissions: %r', missing_mandatory) environ[FACEBOOK_CONNECT_REPOZE_WHO_MISSING_MANDATORY] = \ missing_mandatory response.status = BAD_REQUEST self._set_handler(environ, response) return None else: # Legacy, against FB API < v2.0: if 'email' not in permissions: environ[REPOZE_WHO_LOGGER].warn( 'No permissions to access email address, ' 'will redirect to permission dialog.') self._redirect_to_perms_dialog(environ, login_handler_url, perms=['email']) return None except facebook.GraphAPIError as e: self._log_graph_api_exception('Exception in get_object()', e, environ) raise profile['access_token'] = fb_user['access_token'] environ[REPOZE_WHO_LOGGER].info('graph.get_object("me") = %r', profile) if self.identified_hook is None: # or (fb_user is None): environ[REPOZE_WHO_LOGGER] \ .warn('identify(): No identified_hook was provided.') self._redirect(environ, target_url=default_target_url, response=response) return None self._set_handler(environ, response) identity = {} self.identified_hook(profile['id'], profile, environ, identity, response) return identity
def identify(self, environ): """This method is called when a request is incoming. If credentials are found, the returned identity mapping will contain an arbitrary set of key/value pairs. Return None to indicate that the plugin found no appropriate credentials. An IIdentifier plugin is also permitted to ``preauthenticate'' an identity. If the identifier plugin knows that the identity is ``good'' (e.g. in the case of ticket-based authentication where the userid is embedded into the ticket), it can insert a special key into the identity dictionary: repoze.who.userid. If this key is present in the identity dictionary, no authenticators will be asked to authenticate the identity. """ request = Request(environ) # First test for logout as we then don't need the rest. if request.path in self.logout_handler_paths: if request.path.endswith('.json'): self._logout_json(environ) return None self._logout_and_redirect(environ) return None # No identity was found. # Then we check that we are actually on the URL which is # supposed to be the url to return to (login_handler_path in # configuration) this URL is used for both: the answer for the # login form and when the openid provider redirects the user # back. elif request.path != self.login_handler_path: return None try: request.scheme = request.headers['X-Forwarded-Proto'] except KeyError: pass redirect_to_self_url = request.application_url + \ self.login_handler_path if 'access_token' in request.params and 'uid' in request.params: fb_user = { 'access_token': request.params['access_token'], 'uid': request.params['uid'], } data_source = 'from params' elif 'code' in request.params: try: fb_user = facebook.get_access_token_from_code( request.params['code'], redirect_to_self_url, config['pyfacebook.appid'], config['pyfacebook.secret']) except facebook.GraphAPIError as e: self._log_graph_api_exception( 'Exception in get_access_token_from_code()', e, environ) self._redirect_to(environ) return None data_source = 'via Facebook "code"' else: try: fb_user = facebook.get_user_from_cookie( request.cookies, config['pyfacebook.appid'], config['pyfacebook.secret']) except facebook.GraphAPIError as e: self._log_graph_api_exception( 'Exception in get_user_from_cookie()', e, environ) # Redirect to Facebook to get a code for a new access token. self._redirect_to_perms_dialog(environ, redirect_to_self_url) return None data_source = 'from cookie' environ[REPOZE_WHO_LOGGER] \ .info('Received fb_user = %r (%s)', fb_user, data_source) # Store a local instance of the user data so we don't need # a round-trip to Facebook on every request try: graph = facebook.GraphAPI(fb_user["access_token"]) profile = graph.get_object("me") if not 'id' in profile: environ[REPOZE_WHO_LOGGER] \ .warn('Facebook Python-SDK received no uid.') self._logout_and_redirect(environ) return None if 'uid' in fb_user: assert profile['id'] == fb_user['uid'] else: fb_user['uid'] = profile['id'] permissions = graph.get_permissions() if not 'email' in permissions: environ[REPOZE_WHO_LOGGER].warn( 'No permissions to access email address, ' 'will redirect to permission dialog.') self._redirect_to_perms_dialog(environ, redirect_to_self_url, perms=['email']) return None except facebook.GraphAPIError as e: self._log_graph_api_exception( 'Exception in get_object()', e, environ) raise profile['access_token'] = fb_user['access_token'] environ[REPOZE_WHO_LOGGER] \ .warn('graph.get_object("me") = %r', profile) if self.identified_hook is None: # or (fb_user is None): environ[REPOZE_WHO_LOGGER] \ .warn('identify(): No identified_hook was provided.') self._redirect_to(environ, None) return None identity = dict() authenticated, redirect_to_url, cookies \ = self.identified_hook(profile['id'], profile, environ, identity) self._redirect_to(environ, redirect_to_url, cookies) return identity