class DrupalAuthMiddleware(object): '''Allows CKAN user to login via Drupal. It looks for the Drupal cookie and gets user details from Drupal using XMLRPC. so works side-by-side with normal CKAN logins.''' def __init__(self, app, app_conf): self.app = app self.drupal_client = None self._user_name_prefix = 'user_d' minutes_between_checking_drupal_cookie = app_conf.get('minutes_between_checking_drupal_cookie', 30) self.seconds_between_checking_drupal_cookie = int(minutes_between_checking_drupal_cookie) * 60 # if that int() raises a ValueError then the app will not start def _parse_cookies(self, environ): is_ckan_cookie = [False] drupal_session_id = [False] server_name = environ['SERVER_NAME'] for k, v in environ.items(): key = k.lower() if key == 'http_cookie': is_ckan_cookie[0] = self._is_this_a_ckan_cookie(v) drupal_session_id[0] = self._drupal_cookie_parse(v, server_name) is_ckan_cookie = is_ckan_cookie[0] drupal_session_id = drupal_session_id[0] return is_ckan_cookie, drupal_session_id @staticmethod def _drupal_cookie_parse(cookie_string, server_name): '''Returns the Drupal Session ID from the cookie string.''' cookies = Cookie.SimpleCookie() try: cookies.load(str(cookie_string)) except Cookie.CookieError: log.error("Received invalid cookie: %s" % cookie_string) return False similar_cookies = [] for cookie in cookies: if cookie.startswith('SESS'): # Drupal 6 uses md5, Drupal 7 uses sha256 server_hash = hashlib.sha256(server_name).hexdigest()[:32] if cookie == 'SESS%s' % server_hash: log.debug('Drupal cookie found for server request %s', server_name) return cookies[cookie].value else: similar_cookies.append(cookie) if similar_cookies: log.debug('Drupal cookies ignored with incorrect hash for server %r: %r', server_name, similar_cookies) return None @staticmethod def _is_this_a_ckan_cookie(cookie_string): cookies = Cookie.SimpleCookie() try: cookies.load(str(cookie_string)) except Cookie.CookieError: log.warning("Received invalid cookie: %s" % cookie_string) return False if not 'auth_tkt' in cookies: return False return True def _munge_drupal_id_to_ckan_user_name(self, drupal_id): drupal_id.lower().replace(' ', '_') return u'%s%s' % (self._user_name_prefix, drupal_id) def _log_out(self, environ, new_headers): # don't progress the user info for this request environ['REMOTE_USER'] = None environ['repoze.who.identity'] = None # tell auth_tkt to logout whilst adding the header to tell # the browser to delete the cookie identity = {} headers = environ['repoze.who.plugins']['dgu_auth_tkt'].forget(environ, identity) if headers: new_headers.extend(headers) # Remove cookie from request, so that if we are doing a login again in this request then # it is aware of the cookie removal #log.debug('Removing cookies from request: %r', environ.get('HTTP_COOKIE', '')) cookies = environ.get('HTTP_COOKIE', '').split('; ') cookies = '; '.join([cookie for cookie in cookies if not cookie.startswith('auth_tkt=')]) environ['HTTP_COOKIE'] = cookies #log.debug('Cookies in request now: %r', environ['HTTP_COOKIE']) log.debug('Logged out Drupal user') def __call__(self, environ, start_response): ''' Middleware that sets the CKAN logged-in/logged-out status according to Drupal's logged-in/logged-out status. Every request comes through here before hitting CKAN because it is configured as middleware. ''' new_headers = [] self.do_drupal_login_logout(environ, new_headers) #log.debug('New headers: %r', new_headers) def cookie_setting_start_response(status, headers, exc_info=None): if headers: headers.extend(new_headers) else: headers = new_headers return start_response(status, headers, exc_info) new_start_response = cookie_setting_start_response return self.app(environ, new_start_response) def do_drupal_login_logout(self, environ, new_headers): '''Looks at cookies and auth_tkt and may tell auth_tkt to log-in or log-out to a Drupal user.''' is_ckan_cookie, drupal_session_id = self._parse_cookies(environ) # Is there a Drupal cookie? We may want to do a log-in for it. if drupal_session_id: # Look at any authtkt logged in user details authtkt_identity = environ.get('repoze.who.identity') if authtkt_identity: authtkt_user_name = authtkt_identity['repoze.who.userid'] #same as environ.get('REMOTE_USER', '') authtkt_drupal_session_id = authtkt_identity['userdata'] else: authtkt_user_name = '' authtkt_drupal_session_id = '' if not authtkt_user_name: # authtkt not logged in, so log-in with the Drupal cookie self._do_drupal_login(environ, drupal_session_id, new_headers) return elif authtkt_user_name.startswith(self._user_name_prefix): # A drupal user is logged in with authtkt. # See if that the authtkt matches the drupal cookie's session if authtkt_drupal_session_id != drupal_session_id: # Drupal cookie session has changed, so tell authkit to forget the old one # before we do the new login log.debug('Drupal cookie session has changed.') #log.debug('Drupal cookie session has changed from %r to %r.', authtkt_drupal_session_id, drupal_session_id) self._log_out(environ, new_headers) self._do_drupal_login(environ, drupal_session_id, new_headers) #log.debug('Headers on log-out log-in result: %r', new_headers) return else: log.debug('Drupal cookie session stayed the same.') # Drupal cookie session matches the authtkt - leave user logged ini # Just check that authtkt cookie is not too old - in the # mean-time, Drupal may have invalidated the user, for example. if self.is_authtkt_cookie_too_old(authtkt_identity): log.info('Rechecking Drupal cookie') self._log_out(environ, new_headers) self._do_drupal_login(environ, drupal_session_id, new_headers) return else: # There's a Drupal cookie, but user is logged in as a normal CKAN user. # Ignore the Drupal cookie. return elif not drupal_session_id and is_ckan_cookie: # Deal with the case where user is logged out of Drupal # i.e. user WAS were logged in with Drupal and the cookie was # deleted (probably because Drupal logged out) # Is the logged in user a Drupal user? user_name = environ.get('REMOTE_USER', '') if user_name and user_name.startswith(self._user_name_prefix): log.debug('Was logged in as Drupal user %r but Drupal cookie no longer there.', user_name) self._log_out(environ, new_headers) def _do_drupal_login(self, environ, drupal_session_id, new_headers): '''Given a Drupal cookie\'s session ID, check it with Drupal, create/modify the equivalent CKAN user with properties copied from Drupal and log the person in with auth_tkt and its cookie. ''' if self.drupal_client is None: self.drupal_client = DrupalClient() # ask drupal for the drupal_user_id for this session try: drupal_user_id = self.drupal_client.get_user_id_from_session_id(drupal_session_id) except DrupalRequestError, e: log.error('Error checking session with Drupal: %s', e) return if not drupal_user_id: log.debug('Drupal said the session ID found in the cookie is not valid.') return # ask drupal about this user user_properties = self.drupal_client.get_user_properties(drupal_user_id) # see if user already exists in CKAN ckan_user_name = DrupalUserMapping.drupal_id_to_ckan_user_name(drupal_user_id) from ckan import model from ckan.model.meta import Session query = Session.query(model.User).filter_by(name=unicode(ckan_user_name)) if not query.count(): # need to add this user to CKAN date_created = datetime.datetime.fromtimestamp(int(user_properties['created'])) user = model.User( name=ckan_user_name, fullname=unicode(user_properties['name']), # NB may change in Drupal db about=u'qUser account imported from Drupal system.', email=user_properties['mail'], # NB may change in Drupal db created=date_created, ) Session.add(user) Session.commit() log.debug('Drupal user added to CKAN as: %s', user.name) else: user = query.one() log.debug('Drupal user found in CKAN: %s', user.name) self.set_roles(ckan_user_name, user_properties['roles'].values()) # There is a chance that on this request we needed to get authtkt # to log-out. This would have created headers like this: # 'Set-Cookie', 'auth_tkt="INVALID"...' # but since we are about to login again, which will create a header # setting that same cookie, we need to get rid of the invalidation # header first. new_headers[:] = [(key, value) for (key, value) in new_headers \ if (not (key=='Set-Cookie' and value.startswith('auth_tkt="INVALID"')))] #log.debug('Headers reduced to: %r', new_headers) # Ask auth_tkt to remember this user so that subsequent requests # will be authenticated by auth_tkt. # auth_tkt cookie template needs to also go in the response. identity = {'repoze.who.userid': str(ckan_user_name), 'tokens': '', 'userdata': drupal_session_id} headers = environ['repoze.who.plugins']['dgu_auth_tkt'].remember(environ, identity) if headers: new_headers.extend(headers) # Tell app during this request that the user is logged in environ['REMOTE_USER'] = user.name log.debug('Set REMOTE_USER = %r', user.name)
class DrupalAuthMiddleware(object): '''Allows CKAN user to login via Drupal. It looks for the Drupal cookie and gets user details from Drupal using XMLRPC. so works side-by-side with normal CKAN logins.''' def __init__(self, app, app_conf): self.app = app self.drupal_client = None self._user_name_prefix = 'user_d' minutes_between_checking_drupal_cookie = app_conf.get( 'minutes_between_checking_drupal_cookie', 30) self.seconds_between_checking_drupal_cookie = int( minutes_between_checking_drupal_cookie) * 60 # if that int() raises a ValueError then the app will not start def _parse_cookies(self, environ): is_ckan_cookie = [False] drupal_session_id = [False] server_name = environ['SERVER_NAME'] for k, v in environ.items(): key = k.lower() if key == 'http_cookie': is_ckan_cookie[0] = self._is_this_a_ckan_cookie(v) drupal_session_id[0] = self._drupal_cookie_parse( v, server_name) is_ckan_cookie = is_ckan_cookie[0] drupal_session_id = drupal_session_id[0] return is_ckan_cookie, drupal_session_id @staticmethod def _drupal_cookie_parse(cookie_string, server_name): '''Returns the Drupal Session ID from the cookie string.''' cookies = Cookie.SimpleCookie() try: cookies.load(str(cookie_string)) except Cookie.CookieError: log.error("Received invalid cookie: %s" % cookie_string) return False similar_cookies = [] for cookie in cookies: if cookie.startswith('SESS'): server_hash = hashlib.md5(server_name).hexdigest() if cookie == 'SESS%s' % server_hash: log.debug('Drupal cookie found for server request %s', server_name) return cookies[cookie].value else: similar_cookies.append(cookie) if similar_cookies: log.debug( 'Drupal cookies ignored with incorrect hash for server %r: %r', server_name, similar_cookies) return None @staticmethod def _is_this_a_ckan_cookie(cookie_string): cookies = Cookie.SimpleCookie() try: cookies.load(str(cookie_string)) except Cookie.CookieError: log.warning("Received invalid cookie: %s" % cookie_string) return False if not 'auth_tkt' in cookies: return False return True def _munge_drupal_id_to_ckan_user_name(self, drupal_id): drupal_id.lower().replace(' ', '_') return u'%s%s' % (self._user_name_prefix, drupal_id) def _log_out(self, environ, new_headers): # don't progress the user info for this request environ['REMOTE_USER'] = None environ['repoze.who.identity'] = None # tell auth_tkt to logout whilst adding the header to tell # the browser to delete the cookie identity = {} headers = environ['repoze.who.plugins']['dgu_auth_tkt'].forget( environ, identity) if headers: new_headers.extend(headers) # Remove cookie from request, so that if we are doing a login again in this request then # it is aware of the cookie removal #log.debug('Removing cookies from request: %r', environ.get('HTTP_COOKIE', '')) cookies = environ.get('HTTP_COOKIE', '').split('; ') cookies = '; '.join([ cookie for cookie in cookies if not cookie.startswith('auth_tkt=') ]) environ['HTTP_COOKIE'] = cookies #log.debug('Cookies in request now: %r', environ['HTTP_COOKIE']) log.debug('Logged out Drupal user') def __call__(self, environ, start_response): ''' Middleware that sets the CKAN logged-in/logged-out status according to Drupal's logged-in/logged-out status. Every request comes through here before hitting CKAN because it is configured as middleware. ''' new_headers = [] self.do_drupal_login_logout(environ, new_headers) #log.debug('New headers: %r', new_headers) def cookie_setting_start_response(status, headers, exc_info=None): if headers: headers.extend(new_headers) else: headers = new_headers return start_response(status, headers, exc_info) new_start_response = cookie_setting_start_response return self.app(environ, new_start_response) def do_drupal_login_logout(self, environ, new_headers): '''Looks at cookies and auth_tkt and may tell auth_tkt to log-in or log-out to a Drupal user.''' is_ckan_cookie, drupal_session_id = self._parse_cookies(environ) # Is there a Drupal cookie? We may want to do a log-in for it. if drupal_session_id: # Look at any authtkt logged in user details authtkt_identity = environ.get('repoze.who.identity') if authtkt_identity: authtkt_user_name = authtkt_identity[ 'repoze.who.userid'] #same as environ.get('REMOTE_USER', '') authtkt_drupal_session_id = authtkt_identity['userdata'] else: authtkt_user_name = '' authtkt_drupal_session_id = '' if not authtkt_user_name: # authtkt not logged in, so log-in with the Drupal cookie self._do_drupal_login(environ, drupal_session_id, new_headers) return elif authtkt_user_name.startswith(self._user_name_prefix): # A drupal user is logged in with authtkt. # See if that the authtkt matches the drupal cookie's session if authtkt_drupal_session_id != drupal_session_id: # Drupal cookie session has changed, so tell authkit to forget the old one # before we do the new login log.debug('Drupal cookie session has changed.') #log.debug('Drupal cookie session has changed from %r to %r.', authtkt_drupal_session_id, drupal_session_id) self._log_out(environ, new_headers) self._do_drupal_login(environ, drupal_session_id, new_headers) #log.debug('Headers on log-out log-in result: %r', new_headers) return else: log.debug('Drupal cookie session stayed the same.') # Drupal cookie session matches the authtkt - leave user logged ini # Just check that authtkt cookie is not too old - in the # mean-time, Drupal may have invalidated the user, for example. if self.is_authtkt_cookie_too_old(authtkt_identity): log.info('Rechecking Drupal cookie') self._log_out(environ, new_headers) self._do_drupal_login(environ, drupal_session_id, new_headers) return else: # There's a Drupal cookie, but user is logged in as a normal CKAN user. # Ignore the Drupal cookie. return elif not drupal_session_id and is_ckan_cookie: # Deal with the case where user is logged out of Drupal # i.e. user WAS were logged in with Drupal and the cookie was # deleted (probably because Drupal logged out) # Is the logged in user a Drupal user? user_name = environ.get('REMOTE_USER', '') if user_name and user_name.startswith(self._user_name_prefix): log.debug( 'Was logged in as Drupal user %r but Drupal cookie no longer there.', user_name) self._log_out(environ, new_headers) def _do_drupal_login(self, environ, drupal_session_id, new_headers): '''Given a Drupal cookie\'s session ID, check it with Drupal, create/modify the equivalent CKAN user with properties copied from Drupal and log the person in with auth_tkt and its cookie. ''' if self.drupal_client is None: self.drupal_client = DrupalClient() # ask drupal for the drupal_user_id for this session try: drupal_user_id = self.drupal_client.get_user_id_from_session_id( drupal_session_id) except DrupalRequestError, e: log.error('Error checking session with Drupal: %s', e) return if drupal_user_id: # ask drupal about this user user_properties = self.drupal_client.get_user_properties( drupal_user_id) # see if user already exists in CKAN ckan_user_name = DrupalUserMapping.drupal_id_to_ckan_user_name( drupal_user_id) from ckan import model from ckan.model.meta import Session query = Session.query( model.User).filter_by(name=unicode(ckan_user_name)) if not query.count(): # need to add this user to CKAN date_created = datetime.datetime.fromtimestamp( int(user_properties['created'])) user = model.User( name=ckan_user_name, fullname=unicode( user_properties['name']), # NB may change in Drupal db about=u'User account imported from Drupal system.', email=user_properties[ 'mail'], # NB may change in Drupal db created=date_created, ) Session.add(user) Session.commit() log.debug('Drupal user added to CKAN as: %s', user.name) else: user = query.one() log.debug('Drupal user found in CKAN: %s', user.name) self.set_roles(ckan_user_name, user_properties['roles'].values()) # There is a chance that on this request we needed to get authtkt # to log-out. This would have created headers like this: # 'Set-Cookie', 'auth_tkt="INVALID"...' # but since we are about to login again, which will create a header # setting that same cookie, we need to get rid of the invalidation # header first. new_headers[:] = [(key, value) for (key, value) in new_headers \ if (not (key=='Set-Cookie' and value.startswith('auth_tkt="INVALID"')))] #log.debug('Headers reduced to: %r', new_headers) # Ask auth_tkt to remember this user so that subsequent requests # will be authenticated by auth_tkt. # auth_tkt cookie template needs to also go in the response. identity = { 'repoze.who.userid': str(ckan_user_name), 'tokens': '', 'userdata': drupal_session_id } headers = environ['repoze.who.plugins']['dgu_auth_tkt'].remember( environ, identity) if headers: new_headers.extend(headers) # Tell app during this request that the user is logged in environ['REMOTE_USER'] = user.name log.debug('Set REMOTE_USER = %r', user.name) else: log.debug( 'Drupal said the session ID found in the cookie is not valid.')
class AuthAPIMiddleware(object): def __init__(self, app, app_conf): self.app = app self.drupal_client = None def __call__(self, environ, start_response): if self.drupal_client is None: self.drupal_client = DrupalClient() # establish from the cookie whether ckan and drupal are signed in ckan_signed_in = [False] drupal_signed_in = [False] for k, v in environ.items(): key = k.lower() if key == 'http_cookie': ckan_signed_in[0] = is_ckan_signed_in(v) drupal_signed_in[0] = drupal_extract_cookie(v) ckan_signed_in = ckan_signed_in[0] drupal_signed_in = drupal_signed_in[0] environ['drupal.uid'] = None environ['drupal.publishers'] = None new_start_response = start_response if drupal_signed_in and not ckan_signed_in: # get info about the user from drupal and store in environ for # use by main CKAN app user_id = self.drupal_client.get_user_id_from_session_id(drupal_signed_in) res = self.drupal_client.get_user_properties(user_id) environ['drupal.uid'] = res['uid'] environ['drupal.publishers'] = res['publishers'] environ['drupal.name'] = res['name'] from ckan import model from ckan.model.meta import Session def munge(username): username.lower().replace(' ', '_') return username # Add the new Drupal user if they don't already exist. query = Session.query(model.User).filter_by(name=unicode(environ['drupal.uid'])) if not query.count(): user = model.User( name=munge(unicode(environ['drupal.uid'])), fullname=unicode(environ['drupal.name']), about=u'Drupal auto-generated user', ) Session.add(user) Session.commit() else: user = query.one() # We want to store values in the user's cookie, so # prepare the response header with this value, # using auth_tkt to sign it. new_header = environ['repoze.who.plugins']['auth_tkt'].remember( environ, { 'repoze.who.userid': environ['drupal.uid'], 'tokens': '', 'userdata': '', } ) # e.g. new_header = [('Set-Cookie', 'bob=ab48fe; Path=/;')] cookie_template = new_header[0][1].split('; ') cookie_string = '' for name, value in [ ('ckan_apikey', user.apikey), ('ckan_display_name', user.fullname), ('ckan_user', user.name), ]: cookie_string += '; %s="%s"'%(name, value) new_cookie = cookie_template[:] new_cookie[0] = '%s="%s"'%(name, value) new_header.append(('Set-Cookie', str('; '.join(new_cookie)))) # Also need these cookies to work too: # ckan_apikey # Value "3a51edc6-6461-46b8-bfe2-57445cbdeb2b" # Host catalogue.dev.dataco.coi.gov.uk # Path / # Secure No # Expires At End Of Session # # # Name ckan_display_name # Value "James Gardner" # Host catalogue.dev.dataco.coi.gov.uk # Path / # Secure No # Expires At End Of Session # # # Name ckan_user # Value "4466" # Host catalogue.dev.dataco.coi.gov.uk # Path / # Secure No # Expires At End Of Session # @@@ Need to add the headers to the request too so that the rest of the stack can sign the user in. #Cookie: __utma=217959684.178461911.1286034407.1286034407.1286178542.2; __utmz=217959684.1286178542.2.2.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=coi%20london; DRXtrArgs=James+Gardner; DRXtrArgs2=3e174e7f1e1d3fab5ca138c0a023e13a; SESS9854522e7c5dba5831db083c5372623c=4160a72a4d6831abec1ac57d7b5a59eb; auth_tkt="a578c4a0d21bdbde7f80cd271d60b66f4ceabc3f4466!"; ckan_apikey="3a51edc6-6461-46b8-bfe2-57445cbdeb2b"; ckan_display_name="James Gardner"; ckan_user="******" # There is a bug(/feature?) in line 628 of Cookie.py that means # it can't load from unicode strings. This causes Beaker to fail # unless the value here is a string if not environ.get('HTTP_COOKIE'): environ['HTTP_COOKIE'] += str(cookie_string) else: environ['HTTP_COOKIE'] = str(cookie_string[2:]) def cookie_setting_start_response(status, headers, exc_info=None): headers += new_header return start_response(status, headers, exc_info) new_start_response = cookie_setting_start_response return self.app(environ, new_start_response)
class DrupalAuthMiddleware(object): def __init__(self, app, app_conf): self.app = app self.drupal_client = None self._user_name_prefix = 'user_d' def _parse_cookies(self, environ): is_ckan_cookie = [False] drupal_session_id = [False] for k, v in environ.items(): key = k.lower() if key == 'http_cookie': is_ckan_cookie[0] = self._is_this_a_ckan_cookie(v) drupal_session_id[0] = self._drupal_cookie_parse(v) is_ckan_cookie = is_ckan_cookie[0] drupal_session_id = drupal_session_id[0] return is_ckan_cookie, drupal_session_id @staticmethod def _drupal_cookie_parse(cookie_string): '''Returns the Drupal Session ID from the cookie string.''' cookies = Cookie.SimpleCookie() cookies.load(str(cookie_string)) for cookie in cookies: if cookie.startswith('SESS'): log.debug('Drupal cookie found') return cookies[cookie].value return None @staticmethod def _is_this_a_ckan_cookie(cookie_string): cookies = Cookie.SimpleCookie() cookies.load(str(cookie_string)) if not 'auth_tkt' in cookies: return False return True def _munge_drupal_id_to_ckan_user_name(self, drupal_id): drupal_id.lower().replace(' ', '_') return u'%s%s' % (self._user_name_prefix, drupal_id) def _log_out(self, environ, new_headers): #print "### Drupal-auth: _log_out environ before change:" + pprint.pformat(environ) # don't progress the user info for this request environ['REMOTE_USER'] = None environ['repoze.who.identity'] = None environ['REMOTE_USER_DATA'] = None # tell auth_tkt to logout whilst adding the header to tell # the browser to delete the cookie identity = {} headers = environ['repoze.who.plugins']['dgu_auth_tkt'].forget(environ, identity) if headers: new_headers.extend(headers) # Remove cookie from request, so that if we are doing a login again in this request then # it is aware of the cookie removal log.debug('Removing cookies from request: %r', environ.get('HTTP_COOKIE', '')) #AFJ print "Remove cookies : " + environ.get('HTTP_COOKIE', '') cookies = environ.get('HTTP_COOKIE', '').split('; ') cookies = '; '.join([cookie for cookie in cookies if not cookie.startswith('auth_tkt=')]) environ['HTTP_COOKIE'] = cookies log.debug('Cookies in request now: %r', environ['HTTP_COOKIE']) log.debug('Logged out Drupal user') def __call__(self, environ, start_response): new_headers = [] #log.info("INIT drupal_autk:" + pprint.pformat (environ ) ) self.do_drupal_login_logout(environ, new_headers) #log.info("INIT drupal_autk: after login_logout" + pprint.pformat (environ ) ) #log.debug('New headers: %r', new_headers) def cookie_setting_start_response(status, headers, exc_info=None): if headers: headers.extend(new_headers) else: headers = new_headers return start_response(status, headers, exc_info) new_start_response = cookie_setting_start_response return self.app(environ, new_start_response) def do_drupal_login_logout(self, environ, new_headers): '''Looks at cookies and auth_tkt and may tell auth_tkt to log-in or log-out to a Drupal user.''' is_ckan_cookie, drupal_session_id = self._parse_cookies(environ) # Is there a Drupal cookie? We may want to do a log-in for it. if drupal_session_id: # Look at any authtkt logged in user details authtkt_identity = environ.get('repoze.who.identity') if authtkt_identity: authtkt_user_name = authtkt_identity['repoze.who.userid'] #same as environ.get('REMOTE_USER', '') authtkt_drupal_session_id = authtkt_identity['userdata'] else: authtkt_user_name = '' authtkt_drupal_session_id = '' if not authtkt_user_name: # authtkt not logged in, so log-in with the Drupal cookie self._do_drupal_login(environ, drupal_session_id, new_headers) return elif authtkt_user_name.startswith(self._user_name_prefix): # A drupal user is logged in with authtkt. # See if that the authtkt matches the drupal cookie's session if authtkt_drupal_session_id != drupal_session_id: # Drupal cookie session has changed, so tell authkit to forget the old one # before we do the new login log.debug('Drupal cookie session has changed.') #log.debug('Drupal cookie session has changed from %r to %r.', authtkt_drupal_session_id, drupal_session_id) self._log_out(environ, new_headers) # since we are about to login again, we need to get rid of the headers like # ('Set-Cookie', 'auth_tkt="INVALID"...' since we are about to set them again in this # same request.) new_headers[:] = [(key, value) for (key, value) in new_headers \ if (not (key=='Set-Cookie' and value.startswith('auth_tkt="INVALID"')))] #log.debug('Headers reduced to: %r', new_headers) self._do_drupal_login(environ, drupal_session_id, new_headers) #log.debug('Headers on log-out log-in result: %r', new_headers) return else: log.debug('Drupal cookie session stayed the same.') # Drupal cookie session matches the authtkt - leave user logged in return else: # There's a Drupal cookie, but user is logged in as a normal CKAN user. # Ignore the Drupal cookie. return elif not drupal_session_id and is_ckan_cookie: # Deal with the case where user is logged out of Drupal # i.e. user WAS were logged in with Drupal and the cookie was # deleted (probably because Drupal logged out) log.debug("Logged out of Drupal, but is still in CKAN") # Is the logged in user a Drupal user? user_name = environ.get('REMOTE_USER', '') if user_name and user_name.startswith(self._user_name_prefix): log.debug('Was logged in as Drupal user %r but Drupal cookie no longer there.', user_name) #log.info("### drupal.auth: do_drupal_login_logout before log_out:" + pprint.pformat(environ) ) self._log_out(environ, new_headers) #log.info ("### drupal.auth: do_drupal_login_logout AFTER LOGOUT :" + pprint.pformat(environ) ) def _do_drupal_login(self, environ, drupal_session_id, new_headers): if self.drupal_client is None: self.drupal_client = DrupalClient() # ask drupal for the drupal_user_id for this session log.info("Try to get user_Id drupal_session_id== " + drupal_session_id) try: drupal_user = self.drupal_client.get_user_id_from_session_id(drupal_session_id) except Exception , e: log.error('Error checking session with Drupal: %s' , e) return if drupal_user: from ckan import model from ckan.model.meta import Session query = Session.query(model.User).filter_by(name=unicode(drupal_user)) if not query.count(): # need to add this user to CKAN log.error('Drupal user %s not in CKAN ', drupal_user) return else: user = query.one() log.debug('Drupal user found in CKAN: %s', user.name) # Ask auth_tkt to remember this user so that subsequent requests # will be authenticated by auth_tkt. # auth_tkt cookie template needs to also go in the response. identity = {'repoze.who.userid': str(drupal_user), 'tokens': '', 'userdata': drupal_session_id} headers = environ['repoze.who.plugins']['dgu_auth_tkt'].remember(environ, identity) if headers: new_headers.extend(headers) # Tell app during this request that the user is logged in environ['REMOTE_USER'] = user.name log.debug('Set REMOTE_USER = %r', user.name) else: log.debug('Drupal said the session ID found in the cookie is not valid.')
class AuthAPIMiddleware(object): def __init__(self, app, app_conf): self.app = app self.drupal_client = None def __call__(self, environ, start_response): if self.drupal_client is None: self.drupal_client = DrupalClient() # establish from the cookie whether ckan and drupal are signed in ckan_signed_in = [False] drupal_signed_in = [False] for k, v in environ.items(): key = k.lower() if key == 'http_cookie': ckan_signed_in[0] = is_ckan_signed_in(v) drupal_signed_in[0] = drupal_extract_cookie(v) ckan_signed_in = ckan_signed_in[0] drupal_signed_in = drupal_signed_in[0] environ['drupal.uid'] = None environ['drupal.publishers'] = None new_start_response = start_response if drupal_signed_in and not ckan_signed_in: # get info about the user from drupal and store in environ for # use by main CKAN app user_id = self.drupal_client.get_user_id_from_session_id( drupal_signed_in) res = self.drupal_client.get_user_properties(user_id) environ['drupal.uid'] = res['uid'] environ['drupal.publishers'] = res['publishers'] environ['drupal.name'] = res['name'] from ckan import model from ckan.model.meta import Session def munge(username): username.lower().replace(' ', '_') return username # Add the new Drupal user if they don't already exist. query = Session.query( model.User).filter_by(name=unicode(environ['drupal.uid'])) if not query.count(): user = model.User( name=munge(unicode(environ['drupal.uid'])), fullname=unicode(environ['drupal.name']), about=u'Drupal auto-generated user', ) Session.add(user) Session.commit() else: user = query.one() # We want to store values in the user's cookie, so # prepare the response header with this value, # using auth_tkt to sign it. new_header = environ['repoze.who.plugins']['auth_tkt'].remember( environ, { 'repoze.who.userid': environ['drupal.uid'], 'tokens': '', 'userdata': '', }) # e.g. new_header = [('Set-Cookie', 'bob=ab48fe; Path=/;')] cookie_template = new_header[0][1].split('; ') cookie_string = '' for name, value in [ ('ckan_apikey', user.apikey), ('ckan_display_name', user.fullname), ('ckan_user', user.name), ]: cookie_string += '; %s="%s"' % (name, value) new_cookie = cookie_template[:] new_cookie[0] = '%s="%s"' % (name, value) new_header.append(('Set-Cookie', str('; '.join(new_cookie)))) # Also need these cookies to work too: # ckan_apikey # Value "3a51edc6-6461-46b8-bfe2-57445cbdeb2b" # Host catalogue.dev.dataco.coi.gov.uk # Path / # Secure No # Expires At End Of Session # # # Name ckan_display_name # Value "James Gardner" # Host catalogue.dev.dataco.coi.gov.uk # Path / # Secure No # Expires At End Of Session # # # Name ckan_user # Value "4466" # Host catalogue.dev.dataco.coi.gov.uk # Path / # Secure No # Expires At End Of Session # @@@ Need to add the headers to the request too so that the rest of the stack can sign the user in. #Cookie: __utma=217959684.178461911.1286034407.1286034407.1286178542.2; __utmz=217959684.1286178542.2.2.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=coi%20london; DRXtrArgs=James+Gardner; DRXtrArgs2=3e174e7f1e1d3fab5ca138c0a023e13a; SESS9854522e7c5dba5831db083c5372623c=4160a72a4d6831abec1ac57d7b5a59eb; auth_tkt="a578c4a0d21bdbde7f80cd271d60b66f4ceabc3f4466!"; ckan_apikey="3a51edc6-6461-46b8-bfe2-57445cbdeb2b"; ckan_display_name="James Gardner"; ckan_user="******" # There is a bug(/feature?) in line 628 of Cookie.py that means # it can't load from unicode strings. This causes Beaker to fail # unless the value here is a string if not environ.get('HTTP_COOKIE'): environ['HTTP_COOKIE'] += str(cookie_string) else: environ['HTTP_COOKIE'] = str(cookie_string[2:]) def cookie_setting_start_response(status, headers, exc_info=None): headers += new_header return start_response(status, headers, exc_info) new_start_response = cookie_setting_start_response return self.app(environ, new_start_response)