diff --git a/ckanext/googleauth/plugin.py b/ckanext/googleauth/plugin.py index 075158b..3f1adbe 100644 --- a/ckanext/googleauth/plugin.py +++ b/ckanext/googleauth/plugin.py @@ -1,44 +1,30 @@ -'''This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as -published by the Free Software Foundation, either version 3 of the -License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . - - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed.''' - - - # coding=utf-8 import ckan.plugins as plugins import ckan.plugins.toolkit as toolkit from ckan.lib.plugins import DefaultTranslation import json +import logging import uuid + +from google.oauth2 import id_token +from google.auth.transport import requests import pylons import pylons.config as config -import ckan.lib.helpers as helpers -import requests +import ckan.lib.helpers as h import re - +log = logging.getLogger(__name__) #get 'ckan.googleauth_clientid' from ini file def get_clientid(): return config.get('ckan.googleauth_clientid', '') +def get_loginuri(): + site_url = config.get('ckan.site_url') + return '%s/user/login' % (site_url) + + #get ckan.googleauth_hosted_domain from ini file def get_hosted_domain(): return config.get('ckan.googleauth_hosted_domain', '') @@ -71,55 +57,38 @@ class GoogleauthPlugin(plugins.SingletonPlugin, DefaultTranslation): def update_config(self, config_): toolkit.add_template_directory(config_, 'templates') - toolkit.add_public_directory(config_, 'public') - toolkit.add_resource('fanstatic', 'googleauth') #declare new helper functions def get_helpers(self): return {'googleauth_get_clientid': get_clientid, + 'googleauth_get_loginuri': get_loginuri, 'googleauth_get_hosted_domain': get_hosted_domain} - #verify email address within token - def verify_email(self, token): - res = requests.post('https://www.googleapis.com/oauth2/v3/tokeninfo?id_token='+token, verify=True) - - if res.ok: - is_email_verified=json.loads(res.content) - if is_email_verified['email_verified'] == 'true': - email_verified = is_email_verified['email'] - return email_verified - else: - raise GoogleAuthException(is_email_verified) - else: - raise GoogleAuthException(res) - - - #if exist returns ckan user def get_ckanuser(self, user): import ckan.model - user_ckan = ckan.model.User.by_name(user) + user_ckan = ckan.model.User.by_name(user) - if user_ckan: - user_dict = toolkit.get_action('user_show')(data_dict={'id': user_ckan.id}) - return user_dict - else: - return None + if user_ckan: + user_dict = toolkit.get_action('user_show')(data_dict={'id': user_ckan.id}) + return user_dict + else: + return None #generates a strong password def get_ckanpasswd(self): - import datetime - import random + import datetime + import random - passwd = str(random.random())+ datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")+str(uuid.uuid4().hex) - passwd = re.sub(r"\s+", "", passwd, flags=re.UNICODE) - return passwd + passwd = str(random.random())+ datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")+str(uuid.uuid4().hex) + passwd = re.sub(r"\s+", "", passwd, flags=re.UNICODE) + return passwd @@ -139,53 +108,93 @@ def _logout_user(self): del pylons.session['ckanext-google-user'] if 'ckanext-google-email' in pylons.session: del pylons.session['ckanext-google-email'] + if 'ckanext-google-foo' in pylons.session: + del pylons.session['ckanext-google-foo'] pylons.session.save() #at every access the email address is checked. if it is authorized ckan username is created and access is given def login(self): + log.debug('login()') + + # pylons.session['ckanext-google-foo'] = 'bar' + # pylons.session.save() params = toolkit.request.params - if 'id_token' in params: - try: - mail_verified = self.verify_email(params['id_token']) - except GoogleAuthException, e: - toolkit.abort(500) + if 'credential' in params: + log.debug('credential in params') + # https://developers.google.com/identity/gsi/web/reference/js-reference#CredentialResponse + # https://developers.google.com/identity/gsi/web/guides/verify-google-id-token + id_info = id_token.verify_oauth2_token( + params['credential'], requests.Request(), get_clientid() + ) + if id_info.get('hd', '') != get_hosted_domain(): + log.debug('Hosted domain mismatch: aborting...') + toolkit.abort(500) + + email = id_info['email'] + log.debug('Email address: %s' % email) + + user_account = email_to_ckan_user(email) + log.debug('User account: %s' % user_account) - user_account = email_to_ckan_user(mail_verified) + user_ckan = self.get_ckanuser(user_account) - user_ckan = self.get_ckanuser(user_account) + if not user_ckan: + log.debug('Creating CKAN user') + user_ckan = toolkit.get_action('user_create')( + context={'ignore_auth': True}, + data_dict={'email': email, + 'name': user_account, + 'password': self.get_ckanpasswd()}) - if not user_ckan: - user_ckan = toolkit.get_action('user_create')( - context={'ignore_auth': True}, - data_dict={'email': mail_verified, - 'name': user_account, - 'password': self.get_ckanpasswd()}) + pylons.session['ckanext-google-user'] = user_ckan['name'] + pylons.session['ckanext-google-email'] = email - pylons.session['ckanext-google-user'] = user_ckan['name'] - pylons.session['ckanext-google-email'] = mail_verified + #to revoke the Google token uncomment the code below + #pylons.session['ckanext-google-accesstoken'] = params['token'] - #to revoke the Google token uncomment the code below - #pylons.session['ckanext-google-accesstoken'] = params['token'] - pylons.session.save() + log.debug('Saving pylons session... %r' % pylons.session) + pylons.session.save() + log.debug('Session type: %s' % pylons.session.type) + log.debug('Session dir: %s' % pylons.session.data_dir) + log.debug('Session id: %s' % pylons.session.id) + log.debug('Session use_cookies: %s' % pylons.session.use_cookies) + log.debug('Session timeout: %s' % pylons.session.timeout) + log.debug('Redirecting to dashboard') + h.redirect_to('/dashboard') #if someone is logged in will be set the parameter c.user def identify(self): - user_ckan = pylons.session.get('ckanext-google-user') - if user_ckan: - toolkit.c.user = user_ckan + log.debug('identify()') + log.debug('Pylons session... %r' % pylons.session) + log.debug('Session type: %s' % pylons.session.type) + log.debug('Session dir: %s' % pylons.session.data_dir) + log.debug('Session id: %s' % pylons.session.id) + log.debug('Session use_cookies: %s' % pylons.session.use_cookies) + log.debug('Session timeout: %s' % pylons.session.timeout) + user_ckan = pylons.session.get('ckanext-google-user') + log.debug('CKAN User: %r' % user_ckan) + if user_ckan: + toolkit.c.user = user_ckan def logout(self): + log.debug('logout()') self._logout_user() def abort(self, status_code=None, detail='', headers=None, comment=None): + log.debug('abort() status_code=%s' % status_code) + + log.debug('Pylons session... %r' % pylons.session) + + if status_code == 403 or status_code == 404: + return (status_code, detail, headers, comment) self._logout_user() return (status_code, detail, headers, comment) diff --git a/ckanext/googleauth/public/.gitignore b/ckanext/googleauth/public/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/ckanext/googleauth/public/base/css/gbutton.css b/ckanext/googleauth/public/base/css/gbutton.css deleted file mode 100644 index b231399..0000000 --- a/ckanext/googleauth/public/base/css/gbutton.css +++ /dev/null @@ -1,36 +0,0 @@ -#g-signin-button { - display: inline-block; - background: #4285f4; - color: white; - width: 190px; - border-radius: 5px; - white-space: nowrap; -} - -#g-signin-button:hover { - cursor: pointer; -} - -span.label { - font-weight: bold; -} - -span.icon { - background: url('/base/images/g7.png') transparent 50% no-repeat; - display: inline-block; - vertical-align: middle; - width: 42px; - height: 42px; - /*border-right: #2265d4 1px solid;*/ -} - -span.button-text { - display: inline-block; - vertical-align: middle; - padding-left: 32px; - padding-right: 32px; - font-size: 14px; - font-weight: bold; - /* Use the Roboto font that is loaded in the */ - font-family: 'Roboto', sans-serif; -} diff --git a/ckanext/googleauth/public/base/images/g7.png b/ckanext/googleauth/public/base/images/g7.png deleted file mode 100644 index 77ba4a7..0000000 Binary files a/ckanext/googleauth/public/base/images/g7.png and /dev/null differ diff --git a/ckanext/googleauth/public/base/js/googleauth.js b/ckanext/googleauth/public/base/js/googleauth.js deleted file mode 100644 index e1c7515..0000000 --- a/ckanext/googleauth/public/base/js/googleauth.js +++ /dev/null @@ -1,83 +0,0 @@ -/*This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as -published by the Free Software Foundation, either version 3 of the -License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . - - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed.*/ - - -var googleUser = {}; -var cid = getMetaContent('google-signin-client_id'); -var hd = getMetaContent('google-signin-hosted_domain'); -var startApp = function() { - gapi.load('auth2', function(){ - auth2 = gapi.auth2.init({ - client_id: cid, - cookiepolicy: 'single_host_origin', - hosted_domain: hd - }); - var button = document.getElementById('g-signin-button'); - if (button) { - attachSignin(button); - } - }); -}; - - - -function attachSignin(element) { - auth2.attachClickHandler(element, {}, - function(googleUser) { - - var profile = googleUser.getBasicProfile(); - var name = profile.getName(); - var email = profile.getEmail(); - - var response = googleUser.getAuthResponse(); - var id_token = response['id_token']; - var access_token = response['access_token']; - - $.ajax({ - type: 'POST', - url:'/user/login', - data: {name: name, email: email, id_token: id_token, token: access_token}, - success: function(res, status, xhr) { - window.location.replace("/dashboard"); - }, - error: function(xhr, status, err) { - alert("Login failure: " + err); - } - }); - - }, function(error) { - - - }); - -} - - - -/*get content from meta tag*/ -function getMetaContent(propName) { - var metas = document.getElementsByTagName('meta'); - for (i = 0; i < metas.length; i++) { - if (metas[i].getAttribute("name") == propName) { - return metas[i].getAttribute("content"); - } - } - return ""; -} diff --git a/ckanext/googleauth/templates/base.html b/ckanext/googleauth/templates/base.html index 838d0fa..0fafdcb 100644 --- a/ckanext/googleauth/templates/base.html +++ b/ckanext/googleauth/templates/base.html @@ -1,28 +1,6 @@ {% ckan_extends %} - - -{% block styles %} - - - {{ super() }} -{% endblock %} - - -{% block meta %} -{% set ci=h.googleauth_get_clientid() %} -{% set hd=h.googleauth_get_hosted_domain() %} - {{ super() }} - - - -{% endblock %} - - - {% block scripts %} {{ super() }} - - - + {% endblock %} diff --git a/ckanext/googleauth/templates/user/login.html b/ckanext/googleauth/templates/user/login.html index 50bb07e..a2ca356 100644 --- a/ckanext/googleauth/templates/user/login.html +++ b/ckanext/googleauth/templates/user/login.html @@ -13,10 +13,11 @@

{{ _('Log in with Google') }}

-
- - Google -
+ {% set ci=h.googleauth_get_clientid() %} + {% set uri=h.googleauth_get_loginuri() %} +
+
+
diff --git a/setup.py b/setup.py index b17e73a..d43bc4f 100644 --- a/setup.py +++ b/setup.py @@ -59,7 +59,9 @@ # project is installed. For an analysis of "install_requires" vs pip's # requirements files see: # https://packaging.python.org/en/latest/technical.html#install-requires-vs-requirements-files - install_requires=[], + install_requires=[ + 'google-auth<=1.34.0', + ], # If there are data files included in your packages that need to be # installed, specify them here. If using Python 2.6 or less, then these