Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
73aba96
Not logout users when 403 or 404 error occurs
Dec 1, 2021
29cd108
Fix over-indentation
Dec 1, 2021
84b5d87
Merge pull request #1 from aptivate/fix-logout
martinburchell Dec 17, 2021
2a623b2
Enable deprecation warnings for Google sign-in
martinburchell Jan 26, 2023
db72b4a
WIP: Migrate from Google Sign-In to Google Identify Services Library
martinburchell Mar 22, 2023
aace448
Fix template comment
martinburchell Mar 22, 2023
afa8128
Explicitly set data-login_uri
martinburchell Mar 24, 2023
7f4c49b
Fix parameter when verifying token
martinburchell Mar 24, 2023
e3740ae
Remove obsolete requests import
martinburchell Mar 24, 2023
e7a1dd1
Fix CKAN user lookup
martinburchell Mar 24, 2023
5fa3057
Redirect user to dashboard after login
martinburchell Mar 25, 2023
cf3cf99
Redirect to dashboard if user exists
martinburchell Mar 25, 2023
5a1cb91
Try redirecting in login() instead
martinburchell Mar 25, 2023
187c7e3
Remove obsolete public folder
martinburchell Mar 29, 2023
d9e09ee
Remove migration warning cookie
martinburchell Mar 29, 2023
5f0b2c3
Logging
martinburchell Mar 29, 2023
ead99e7
More debugging
martinburchell Mar 29, 2023
29fdf93
Temporarily disable redirect
martinburchell Mar 29, 2023
56c057f
Restore redirect and dump session info
martinburchell Mar 29, 2023
2efcf15
Debug session in identity()
martinburchell Mar 29, 2023
a32d3f7
Debug abort()
martinburchell Mar 29, 2023
2b08851
More debugging
martinburchell Mar 30, 2023
0460e12
No attribute namespace
martinburchell Mar 30, 2023
6d84f79
More session debugging
martinburchell Mar 30, 2023
193c1de
Try creating the session on GET
martinburchell Apr 4, 2023
b6bde01
Try deleting ckanext-google-foo
martinburchell Apr 4, 2023
31ea26b
Remove workaround from previous commit
martinburchell Apr 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 84 additions & 75 deletions ckanext/googleauth/plugin.py
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007

Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
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', '')
Expand Down Expand Up @@ -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



Expand All @@ -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)
Empty file.
36 changes: 0 additions & 36 deletions ckanext/googleauth/public/base/css/gbutton.css

This file was deleted.

Binary file removed ckanext/googleauth/public/base/images/g7.png
Binary file not shown.
83 changes: 0 additions & 83 deletions ckanext/googleauth/public/base/js/googleauth.js

This file was deleted.

24 changes: 1 addition & 23 deletions ckanext/googleauth/templates/base.html
Original file line number Diff line number Diff line change
@@ -1,28 +1,6 @@
{% ckan_extends %}



{% block styles %}
<link rel="stylesheet" type="text/css" href="/base/css/gbutton.css">
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet" type="text/css">
{{ super() }}
{% endblock %}


{% block meta %}
{% set ci=h.googleauth_get_clientid() %}
{% set hd=h.googleauth_get_hosted_domain() %}
{{ super() }}
<meta name="google-signin-scope" content="profile email">
<meta name="google-signin-client_id" content="{{ ci }}">
<meta name="google-signin-hosted_domain" content="{{ hd }}">
{% endblock %}



{% block scripts %}
{{ super() }}
<script src="https://apis.google.com/js/api:client.js"></script>
<script type="text/javascript" src="/base/js/googleauth.js"></script>
<script>startApp();</script>
<script src="https://accounts.google.com/gsi/client" async defer></script>
{% endblock %}
Loading