def department_or_agency_to_organisation(cls, dept_or_agency, include_id=True, organisation_cache=None, drupal_client_cache=None, ): '''Returns None if not found.''' if organisation_cache is None: organisation_cache = {} if not drupal_client_cache: drupal_client_cache = DrupalClient() if dept_or_agency not in organisation_cache: try: organisation_id = drupal_client_cache.match_organisation(dept_or_agency) except DrupalKeyError: name = canonise_organisation_name(dept_or_agency) try: organisation_id = drupal_client_cache.match_organisation(name) except DrupalKeyError: organisation_id = None if organisation_id: organisation_name = drupal_client_cache.get_organisation_name(organisation_id) organisation_cache[dept_or_agency] = (organisation_name, organisation_id) else: organisation_cache[dept_or_agency] = None if not organisation_cache[dept_or_agency]: return None if include_id: return '%s [%s]' % organisation_cache[dept_or_agency] else: return '%s' % organisation_cache[dept_or_agency][0]
def generate(cls, xmlrpc_settings): drupal = DrupalClient(xmlrpc_settings) orgs = {} has_errors = False orgs_to_lookup = set() orgs_to_lookup.add('Northern Ireland Executive') for org_name in orgs_to_lookup: org_name = canonise_organisation_name(org_name) org_id = drupal.match_organisation(org_name) if org_id == False: log.error('Could not find organisation %r', org_name) has_errors = True continue proper_org_name = drupal.get_organisation_name(org_id) parent_department_id = drupal.get_department_from_organisation(org_id) orgs[org_id] = {'name': proper_org_name, 'parent_department_id': parent_department_id} f = open(cls.lots_of_orgs_filepath, 'w') try: f.write(json.dumps(orgs)) finally: f.close() if has_errors: print 'Finished with ERRORS' sys.exit(1) else: print 'Finished with SUCCESS'
def generate(cls, xmlrpc_settings): drupal = DrupalClient(xmlrpc_settings) orgs = {} has_errors = False orgs_to_lookup = set() orgs_to_lookup.add('Northern Ireland Executive') for org_name in orgs_to_lookup: org_name = canonise_organisation_name(org_name) org_id = drupal.match_organisation(org_name) if org_id == False: log.error('Could not find organisation %r', org_name) has_errors = True continue proper_org_name = drupal.get_organisation_name(org_id) parent_department_id = drupal.get_department_from_organisation( org_id) orgs[org_id] = { 'name': proper_org_name, 'parent_department_id': parent_department_id } f = open(cls.lots_of_orgs_filepath, 'w') try: f.write(json.dumps(orgs)) finally: f.close() if has_errors: print 'Finished with ERRORS' sys.exit(1) else: print 'Finished with SUCCESS'
def get_user_realname(user): from ckanext.dgu.drupalclient import DrupalClient from HTMLParser import HTMLParser if user.name.startswith('user_d'): user_id = user.name[len('user_d'):] html_parser = HTMLParser() try: dc = DrupalClient() properties = dc.get_user_properties(user_id) except Exception, ex: return user.fullname try: first_name = properties['field_first_name']['und'][0]['safe_value'] first_name = html_parser.unescape(first_name) except: first_name = '' try: surname = properties['field_surname']['und'][0]['safe_value'] surname = html_parser.unescape(surname) except: surname = ''
def test_match_organisation(self): drupal_config = get_mock_drupal_config() client = DrupalClient() org_id = client.match_organisation('Ealing PCT') assert_equal(org_id, '2') assert_raises(DrupalKeyError, client.match_organisation, '') assert_raises(DrupalKeyError, client.match_organisation, None)
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 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
def test_get_user_properties(self): drupal_config = get_mock_drupal_config() test_user_id = '62' expected_user = drupal_config['test_users'][test_user_id] client = DrupalClient() user = client.get_user_properties(test_user_id) assert user assert isinstance(user, dict) assert_equal(user['name'], expected_user['name']) expected_publishers = expected_user['publishers'] assert_equal(user['publishers'], expected_publishers)
def test_get_department_from_organisation(self): drupal_config = get_mock_drupal_config() client = DrupalClient() parent_org_id = client.get_department_from_organisation('2') assert_equal(parent_org_id, '7') parent_org_id = client.get_department_from_organisation(2) assert_equal(parent_org_id, '7') assert_raises(DrupalKeyError, client.get_department_from_organisation, '999') assert_raises(DrupalKeyError, client.get_department_from_organisation, '') assert_raises(DrupalKeyError, client.get_department_from_organisation, None)
def test_get_organisation(self): drupal_config = get_mock_drupal_config() client = DrupalClient() org_name = client.get_organisation_name('2') assert_equal(org_name, 'Ealing PCT') org_name = client.get_organisation_name(2) assert_equal(org_name, 'Ealing PCT') assert_raises(DrupalKeyError, client.get_organisation_name, '999') assert_raises(DrupalKeyError, client.get_organisation_name, '') assert_raises(DrupalKeyError, client.get_organisation_name, None)
def sync(self, write, user): from ckan import model from ckanext.dgu.drupalclient import DrupalClient, DrupalRequestError from ckanext.dgu.authentication.drupal_auth import DrupalUserMapping log = self.log update_keys = set(('email', 'fullname')) drupal = DrupalClient() users = model.Session.query(model.User)\ .filter_by(state='active')\ .filter(model.User.name.like('user_d%')) if user: users = users.filter(model.User.fullname == user) users = users.all() log.info('Drupal users in CKAN: %s', len(users)) for user in users: drupal_user_id = DrupalUserMapping.ckan_user_name_to_drupal_id( user.name) try: drupal_user = drupal.get_user_properties(drupal_user_id) except DrupalRequestError, e: if 'There is no user with ID' in str(e): log.info( stats.add('Removed deleted user', '%s %s' % (drupal_user_id, user.fullname))) if write: user.delete() continue elif 'Access denied for user' in str(e): log.info( stats.add('Removed blocked user', '%s %s' % (drupal_user_id, user.fullname))) if write: user.delete() continue raise DrupalRequestError user_dict = DrupalUserMapping.drupal_user_to_ckan_user(drupal_user) user_changed = False for key in update_keys: if getattr(user, key) != user_dict[key]: log.info( stats.add( 'Updating field %s' % key, '%s %s %s->%s' % (drupal_user_id, user.fullname, getattr( user, key), user_dict[key]))) if write: setattr(user, key, user_dict[key]) user_changed = True if not user_changed: log.info( stats.add('Unchanged user', '%s %s' % (drupal_user_id, user.fullname)))
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
def command(cls, config_ini): config_ini_filepath = os.path.abspath(config_ini) cls.load_config(config_ini_filepath) engine = engine_from_config(config, 'sqlalchemy.') from ckan import model from ckanext.dgu.drupalclient import DrupalClient logging.config.fileConfig(config_ini_filepath) log = logging.getLogger(os.path.basename(__file__)) global global_log global_log = log model.init_model(engine) model.repo.new_revision() cls.drupal_client = DrupalClient({'xmlrpc_domain': 'data.gov.uk', 'xmlrpc_username': '******', 'xmlrpc_password': config.get('dgu.xmlrpc_password')}) publisher_dicts = cls.drupal_client.get_organisation_list() for publisher_dict in publisher_dicts: if not (publisher_dict['status'] == '1' or \ publisher_dict['nid'] == '16248'): # Make an exception for 16248 - Met Office under BIS is correct log.info('Ignoring unpublished publisher with status %r: %r', publisher_dict['status'], publisher_dict) continue cls.add_publisher(publisher_dict['nid']) all_groups = model.Session.query(model.Group).\ filter(model.Group.type == 'organization').order_by('title').all() log.info('Total number of groups: %i', len(all_groups)) log.info('Warnings: %r', warnings)
def test_sync_one(self): groups = model.Session.query(model.Group) assert groups.count() == 0 rev = model.repo.new_revision() rev.author = 'okfn_maintenance' rev.message = 'Syncing organisations.' drupal_client = DrupalClient() sync.sync_organisation(drupal_client, '16203') #HESA model.repo.commit_and_remove() groups = model.Session.query(model.Group) assert_equal(groups.count(), 2) group_hesa = model.Group.get(u'higher-education-statistics-agency') group_bis = model.Group.get( u'department-for-business-innovation-and-skills') assert group_hesa assert group_bis assert_equal(group_hesa.title, 'Higher Education Statistics Agency') assert_equal(group_hesa.extras['drupal_id'], '16203') assert_equal(group_hesa.extras['department_id'], group_bis.id) assert_equal(group_bis.title, 'Department for Business, Innovation and Skills') assert_equal(group_bis.extras['drupal_id'], '11399') assert_equal(group_bis.extras['department_id'], group_bis.id)
def sync(self, write, user): from ckan import model from ckanext.dgu.drupalclient import DrupalClient, DrupalRequestError from ckanext.dgu.authentication.drupal_auth import DrupalUserMapping log = self.log update_keys = set(('email', 'fullname')) drupal = DrupalClient() users = model.Session.query(model.User)\ .filter_by(state='active')\ .filter(model.User.name.like('user_d%')) if user: users = users.filter(model.User.fullname == user) users = users.all() log.info('Drupal users in CKAN: %s', len(users)) for user in users: drupal_user_id = DrupalUserMapping.ckan_user_name_to_drupal_id(user.name) try: drupal_user = drupal.get_user_properties(drupal_user_id) except DrupalRequestError, e: if 'There is no user with ID' in str(e): log.info(stats.add('Removed deleted user', '%s %s' % (drupal_user_id, user.fullname))) if write: user.delete() continue elif 'Access denied for user' in str(e): log.info(stats.add('Removed blocked user', '%s %s' % (drupal_user_id, user.fullname))) if write: user.delete() continue raise DrupalRequestError user_dict = DrupalUserMapping.drupal_user_to_ckan_user(drupal_user) user_changed = False for key in update_keys: if getattr(user, key) != user_dict[key]: log.info(stats.add( 'Updating field %s' % key, '%s %s %s->%s' % (drupal_user_id, user.fullname, getattr(user, key), user_dict[key]))) if write: setattr(user, key, user_dict[key]) user_changed = True if not user_changed: log.info(stats.add('Unchanged user', '%s %s' % (drupal_user_id, user.fullname)))
def command(cls, config_ini): config_ini_filepath = os.path.abspath(config_ini) cls.load_config(config_ini_filepath) engine = engine_from_config(config, 'sqlalchemy.') from ckan import model from ckanext.dgu.drupalclient import DrupalClient, DrupalRequestError import ckanext.dgu.drupalclient logging.config.fileConfig(config_ini_filepath) log = logging.getLogger(os.path.basename(__file__)) global global_log global_log = log model.init_model(engine) model.repo.new_revision() # disable xmlrpc logs ckanext.dgu.drupalclient.log.disabled = True cls.drupal_client = DrupalClient({ 'xmlrpc_domain': 'data.gov.uk', 'xmlrpc_username': '******', 'xmlrpc_password': config.get('dgu.xmlrpc_password') }) f = open('users.csv', 'wb') users = csv.writer(f, quoting=csv.QUOTE_ALL) rows = [] for nid in range(28, 35000): try: user = cls.drupal_client.get_user_properties(nid) except DrupalRequestError, e: if '404' in str(e): # node not a user continue else: raise publishers = user['publishers'] if len(publishers) > 1: log.info('Multiple publishers for user %s [%s]!: %r', user['name'], user['uid'], repr(publishers)[:100]) if len(publishers) > 100: warn('Ignoring user %s [%s] with %i publishers!', user['name'], user['uid'], len(publishers)) continue for publisher in publishers: row = [user['uid'], user['name'], user['mail'], publisher] rows.append(row) log.info('User: %r', row) users.writerow(row) f.flush()
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 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
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
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
def test_get_url(self): assert config['dgu.xmlrpc_domain'] url = DrupalClient.get_xmlrpc_url() assert_equal(url, MOCK_DRUPAL_URL)
def test_get_url(self): assert config['dgu.xmlrpc_domain'] url, url_logsafe = DrupalClient.get_xmlrpc_url() assert_equal(url, MOCK_DRUPAL_URL) assert_equal(url_logsafe, MOCK_DRUPAL_URL) # because there's no password
def _drupal_client(cls): if not hasattr(cls, '_drupal_client_cache'): cls._drupal_client_cache = DrupalClient() return cls._drupal_client_cache
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)
def __init__(self, xmlrpc_settings=None): self._drupal_client_cache = DrupalClient(xmlrpc_settings) self._organisation_cache = {} # {dept_or_agency:('name', 'id')}
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): '''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 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)
load_config(config_ini_filepath) register_translator() logging.config.fileConfig(config_ini_filepath) log = logging.getLogger(os.path.basename(__file__)) # import CKAN here, rather than earlier, else their logging wont work from ckan import model from ckanext.dgu.lib import publisher as publisher_lib from ckanext.dgu.drupalclient import DrupalClient, DrupalRequestError from pylons import config from running_stats import StatsList # Drupal API drupal = DrupalClient({ 'xmlrpc_domain': 'data.gov.uk', 'xmlrpc_username': config.get('dgu.xmlrpc_username'), 'xmlrpc_password': config.get('dgu.xmlrpc_password') }) user_emails = {} # name:email def get_email_for_user(user): if user.name not in user_emails: if 'user_d' in user.name: user_drupal_id = user.name.replace('user_d', '') try: user_properties = drupal.get_user_properties(user_drupal_id) except DrupalRequestError, e: user_emails[user.name] = user.email else:
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.')
def sync(xmlrpc_settings): drupal_client = DrupalClient(xmlrpc_settings) for org_id in range(10000, 20000): sync_organisation(drupal_client, org_id)