Skip to content

Commit

Permalink
Merge pull request #3 from keitaroinc/login
Browse files Browse the repository at this point in the history
Login
  • Loading branch information
JGulic authored Mar 23, 2023
2 parents d183eb7 + cec5aae commit 5acdc7d
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 38 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ To install ckanext-keycloak:
Configuration settings to run the extension
```
ckan.sso.keycloak_url = link_to_keycloack_authentication_url
ckan.sso.keycloak_realm = realm_name
ckan.sso.keycloak_client_id = client_id
ckan.sso.redirect_uri = redirect_url
ckanext.keycloak_url = link_to_keycloack_authentication_url
ckanext.keycloak.client_id = realm_name
ckanext.keycloak.realm_name = client_id
ckanext.keycloak.redirect_uri = redirect_url
ckanext.keycloak.client_secret_key = client_secret_key
```
## Developer installation
Expand Down
16 changes: 8 additions & 8 deletions ckanext/keycloak/keycloak.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
import logging
from keycloak import KeycloakOpenID, KeycloakAdmin

log = logging.getLogger(__name__)

class KeycloakClient:
def __init__(self, server_url, client_id, realm_name, client_secret):
def __init__(self, server_url, client_id, realm_name, client_secret_key):
self.server_url = server_url
self.client_id = client_id
self.realm_name = realm_name
self.client_secret = client_secret

self.client_secret_key = client_secret_key
def get_keycloak_client(self):
return KeycloakOpenID(
server_url=self.server_url, client_id=self.client_id, realm_name=self.realm_name
server_url=self.server_url, client_id=self.client_id, realm_name=self.realm_name, client_secret_key=self.client_secret_key
)

def get_auth_url(self, redirect_uri):
return self.get_keycloak_client().auth_url(redirect_uri=redirect_uri)
return self.get_keycloak_client().auth_url(redirect_uri=redirect_uri, scope="openid profile email")

def get_token(self, code, redirect_uri):
return self.get_keycloak_client().token(grant_type="authorization_code", code=code, redirect_uri=redirect_uri)

def get_user_info(self, token):
return self.get_keycloak_client().userinfo(token)
print (token.get('access_token'))
return self.get_keycloak_client().userinfo(token.get('access_token'))

def get_user_groups(self, token):
return self.get_keycloak_client().userinfo(token).get('groups', [])

def get_keycloak_admin(self):
return KeycloakAdmin(
username="admin",
)
)
69 changes: 43 additions & 26 deletions ckanext/keycloak/views.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,59 @@
import logging

from flask import Blueprint, session

from ckan.plugins import toolkit as tk
import ckan.lib.helpers as h
import ckan.model as model
from ckan.common import g
from ckan.views.user import set_repoze_user, RequestResetView

from ckanext.keycloak.keycloak import KeycloakClient
import ckanext.keycloak.helpers as helpers
from urllib.parse import urlencode
import ckan.lib.dictization as dictization
from os import environ

log = logging.getLogger(__name__)

keycloak = Blueprint('keycloak', __name__, url_prefix='/user')


server_url = tk.config.get('sso.keycloak_url', 'https://auth.sproutopencontent.com/')
client_id = tk.config.get('sso.keycloak_client_id', 'sprout-client')
realm_name = tk.config.get('sso.keycloak_realm', 'sprout')
redirect_uri = tk.config.get('sso.redirect_uri', None)
client_secret = tk.config.get('sso.keycloak_client_secret', None)
client = KeycloakClient(server_url, client_id, realm_name, client_secret)
server_url = tk.config.get('ckanext.keycloak.server_url', environ.get('CKANEXT__KEYCLOAK__SERVER_URL'))
client_id = tk.config.get('ckanext.keycloak.client_id', environ.get('CKANEXT__KEYCLOAK__CLIENT_ID'))
realm_name = tk.config.get('ckanext.keycloak.realm_name', environ.get('CKANEXT__KEYCLOAK__REALM_NAME'))
redirect_uri = tk.config.get('ckanext.keycloak.redirect_uri', environ.get('CKANEXT__KEYCLOAK__REDIRECT_URI'))
client_secret_key = tk.config.get('ckanext.keycloak.client_secret_key', environ.get('CKANEXT__KEYCLOAK__CLIENT_SECRET_KEY'))

client = KeycloakClient(server_url, client_id, realm_name, client_secret_key)

def _create_user(user_dict):
context = {
u'ignore_auth': True,
}

try:
return tk.get_action(u'user_create')(context, user_dict)
except tk.ValidationError as e:
error_message = (e.error_summary or e.message or e.error_dict)
tk.abort(400, error_message)

def _log_user_into_ckan(resp):
""" Log the user into different CKAN versions.
CKAN 2.10 introduces flask-login and login_user method.
CKAN 2.9.6 added a security change and identifies the user
with the internal id plus a serial autoincrement (currently static).
CKAN <= 2.9.5 identifies the user only using the internal id.
"""
if tk.check_ckan_version(min_version="2.10"):
from ckan.common import login_user
login_user(g.userobj)
return

if tk.check_ckan_version(min_version="2.9.6"):
user_id = "{},1".format(g.userobj.get('id'))
else:
user_id = g.userobj['name']
set_repoze_user(user_id, resp)

log.info(u'User {0}<{1}> logged in successfully'.format(g.userobj['name'], g.userobj['email']))

def sso():
log.info("SSO Login")
auth_url = None
Expand All @@ -43,15 +62,12 @@ def sso():
except Exception as e:
log.error("Error getting auth url: {}".format(e))
return tk.abort(500, "Error getting auth url: {}".format(e))

return tk.redirect_to(auth_url)


def sso_login():
data = tk.request.args
token = client.get_token(data['code'], redirect_uri)
userinfo = client.get_user_info(token.get('access_token'))

userinfo = client.get_user_info(token)
log.info("SSO Login: {}".format(userinfo))
if userinfo:
user_dict = {
Expand All @@ -63,27 +79,29 @@ def sso_login():
'idp': 'google'
}
}
context = {"model": model, "session": model.Session}
user = helpers.process_user(user_dict)
g.userobj = model.User.get(user['name'])
g.user = user
user_id = "{},1".format(user.get('id'))
response = tk.redirect_to(tk.url_for('dashboard.index'))
set_repoze_user(user_id, response)

log.info(u'User {0}<{1}> logged in successfully'.format(g.userobj.name, g.userobj.email))
g.userobj = user
context['user'] = user
context['auth_user_obj'] = g.userobj
user_id = "{}".format(user.get('name'))

response = tk.redirect_to(tk.url_for('user.me',context))

_log_user_into_ckan(response)
log.info("Logged in success")
return response
return tk.redirect_to(tk.url_for('user.login'))
else:
return tk.redirect_to(tk.url_for('user.login'))

def reset_password():
email = tk.request.form.get('user', None)

if '@' not in email:
log.info(f'User requested reset link for invalid email: {email}')
h.flash_error('Invalid email address')
return tk.redirect_to(tk.url_for('user.request_reset'))

user = model.User.by_email(email)

user = model.User.by_email(email)
if not user:
log.info(u'User requested reset link for unknown user: {}'.format(email))
return tk.redirect_to(tk.url_for('user.login'))
Expand All @@ -94,7 +112,6 @@ def reset_password():
return tk.redirect_to(tk.url_for('user.login'))
return RequestResetView().post()


keycloak.add_url_rule('/sso', view_func=sso)
keycloak.add_url_rule('/sso_login', view_func=sso_login)
keycloak.add_url_rule('/reset_password', view_func=reset_password, methods=['POST'])
Expand Down

0 comments on commit 5acdc7d

Please sign in to comment.