-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
flask and python3 conversion #42
base: master
Are you sure you want to change the base?
Changes from 3 commits
e33ff06
390bdb6
3633240
223175f
9ff9977
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
import click | ||
import ckanext.oauth2.utils as utils | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
|
||
@click.group() | ||
def oauth2(): | ||
"""Oauth2 management commands. | ||
""" | ||
pass | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it needed? |
||
|
||
|
||
def get_commands(): | ||
return [oauth2] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
CAME_FROM_FIELD = 'came_from' | ||
INITIAL_PAGE = '/dashboard' | ||
INITIAL_PAGE = '/' | ||
REDIRECT_URL = 'oauth2/callback' |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,16 +18,16 @@ | |
# You should have received a copy of the GNU Affero General Public License | ||
# along with OAuth2 CKAN Extension. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
from __future__ import unicode_literals | ||
# from __future__ import unicode_literals | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't be dropped entirely? |
||
|
||
import logging | ||
import constants | ||
from ckanext.oauth2 import constants | ||
|
||
from ckan.common import session | ||
import ckan.lib.helpers as helpers | ||
import ckan.lib.base as base | ||
import ckan.plugins.toolkit as toolkit | ||
import oauth2 | ||
from ckanext.oauth2 import oauth2 | ||
|
||
from ckanext.oauth2.plugin import _get_previous_page | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,10 +18,17 @@ | |
# along with OAuth2 CKAN Extension. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
import sqlalchemy as sa | ||
import ckan.model.meta as meta | ||
import logging | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no logging in this file, except a |
||
from ckan.model.domain_object import DomainObject | ||
from sqlalchemy.ext.declarative import declarative_base | ||
|
||
UserToken = None | ||
log = logging.getLogger(__name__) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not used, can be discarded. |
||
|
||
Base = declarative_base() | ||
metadata = Base.metadata | ||
|
||
UserToken = None | ||
def init_db(model): | ||
|
||
global UserToken | ||
|
@@ -47,3 +54,5 @@ def by_user_name(cls, user_name): | |
user_token_table.create(checkfirst=True) | ||
|
||
model.meta.mapper(UserToken, user_token_table) | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These empty lines could be avoided. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,11 +19,10 @@ | |
# along with OAuth2 CKAN Extension. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
|
||
from __future__ import unicode_literals | ||
|
||
import base64 | ||
import ckan.model as model | ||
import db | ||
from ckanext.oauth2.db import UserToken | ||
import ckanext.oauth2.db as db | ||
import json | ||
import logging | ||
from six.moves.urllib.parse import urljoin | ||
|
@@ -38,23 +37,24 @@ | |
|
||
import jwt | ||
|
||
import constants | ||
from .constants import * | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wildcard import are a bit dangerous and potentially confusing, but they are not a big issue. |
||
from flask import Flask, request, redirect, session, url_for, jsonify | ||
|
||
|
||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
def generate_state(url): | ||
return b64encode(bytes(json.dumps({constants.CAME_FROM_FIELD: url}))) | ||
return b64encode(bytes(json.dumps({CAME_FROM_FIELD: url}).encode())) | ||
|
||
|
||
def get_came_from(state): | ||
return json.loads(b64decode(state)).get(constants.CAME_FROM_FIELD, '/') | ||
return json.loads(b64decode(state)).get(CAME_FROM_FIELD, '/') | ||
|
||
|
||
REQUIRED_CONF = ("authorization_endpoint", "token_endpoint", "client_id", "client_secret", "profile_api_url", "profile_api_user_field", "profile_api_mail_field") | ||
|
||
|
||
class OAuth2Helper(object): | ||
|
||
def __init__(self): | ||
|
@@ -79,10 +79,7 @@ def __init__(self): | |
self.profile_api_groupmembership_field = six.text_type(os.environ.get('CKAN_OAUTH2_PROFILE_API_GROUPMEMBERSHIP_FIELD', toolkit.config.get('ckan.oauth2.profile_api_groupmembership_field', ''))).strip() | ||
self.sysadmin_group_name = six.text_type(os.environ.get('CKAN_OAUTH2_SYSADMIN_GROUP_NAME', toolkit.config.get('ckan.oauth2.sysadmin_group_name', ''))).strip() | ||
|
||
self.redirect_uri = urljoin(urljoin(toolkit.config.get('ckan.site_url', 'http://localhost:5000'), toolkit.config.get('ckan.root_path')), constants.REDIRECT_URL) | ||
|
||
# Init db | ||
db.init_db(model) | ||
self.redirect_uri = urljoin(urljoin(toolkit.config.get('ckan.site_url', 'http://localhost:5000'), toolkit.config.get('ckan.root_path')), REDIRECT_URL) | ||
|
||
missing = [key for key in REQUIRED_CONF if getattr(self, key, "") == ""] | ||
if missing: | ||
|
@@ -93,11 +90,12 @@ def __init__(self): | |
def challenge(self, came_from_url): | ||
# This function is called by the log in function when the user is not logged in | ||
state = generate_state(came_from_url) | ||
# log.debug(f'redirect uri: {self.redirect_uri}') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be removed. |
||
oauth = OAuth2Session(self.client_id, redirect_uri=self.redirect_uri, scope=self.scope, state=state) | ||
auth_url, _ = oauth.authorization_url(self.authorization_endpoint) | ||
log.debug('Challenge: Redirecting challenge to page {0}'.format(auth_url)) | ||
# CKAN 2.6 only supports bytes | ||
return toolkit.redirect_to(auth_url.encode('utf-8')) | ||
return toolkit.redirect_to(auth_url)#.encode('utf-8')) | ||
|
||
def get_token(self): | ||
oauth = OAuth2Session(self.client_id, redirect_uri=self.redirect_uri, scope=self.scope) | ||
|
@@ -111,41 +109,42 @@ def get_token(self): | |
if self.legacy_idm: | ||
# This is only required for Keyrock v6 and v5 | ||
headers['Authorization'] = 'Basic %s' % base64.urlsafe_b64encode( | ||
'%s:%s' % (self.client_id, self.client_secret) | ||
(f'{self.client_id}:{self.client_secret}').encode() | ||
) | ||
|
||
try: | ||
log.debug(f'authorization_response: {toolkit.request.url}') | ||
token = oauth.fetch_token(self.token_endpoint, | ||
headers=headers, | ||
client_id=self.client_id, | ||
client_secret=self.client_secret, | ||
authorization_response=toolkit.request.url, | ||
verify=self.verify_https) | ||
authorization_response=toolkit.request.url.replace('http:', 'https:', 1)) | ||
except requests.exceptions.SSLError as e: | ||
# TODO search a better way to detect invalid certificates | ||
if "verify failed" in six.text_type(e): | ||
raise InsecureTransportError() | ||
else: | ||
raise | ||
|
||
return token | ||
|
||
def identify(self, token): | ||
|
||
if self.jwt_enable: | ||
|
||
log.debug('jwt_enabled') | ||
access_token = bytes(token['access_token']) | ||
user_data = jwt.decode(access_token, verify=False) | ||
user = self.user_json(user_data) | ||
else: | ||
|
||
else: | ||
try: | ||
if self.legacy_idm: | ||
profile_response = requests.get(self.profile_api_url + '?access_token=%s' % token['access_token'], verify=self.verify_https) | ||
log.debug(f'profile response: {profile_response}') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we keep this? |
||
else: | ||
oauth = OAuth2Session(self.client_id, token=token) | ||
profile_response = oauth.get(self.profile_api_url, verify=self.verify_https) | ||
profile_response = oauth.get(self.profile_api_url) | ||
log.debug(f'profile response_: {profile_response}') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we keep this? |
||
|
||
except requests.exceptions.SSLError as e: | ||
log.debug('exception identify oauth2') | ||
# TODO search a better way to detect invalid certificates | ||
if "verify failed" in six.text_type(e): | ||
raise InsecureTransportError() | ||
|
@@ -162,6 +161,7 @@ def identify(self, token): | |
else: | ||
user_data = profile_response.json() | ||
user = self.user_json(user_data) | ||
log.debug(f'user: {user}') | ||
|
||
# Save the user in the database | ||
model.Session.add(user) | ||
|
@@ -171,6 +171,7 @@ def identify(self, token): | |
return user.name | ||
|
||
def user_json(self, user_data): | ||
log.debug(f'user_data: {user_data}') | ||
email = user_data[self.profile_api_mail_field] | ||
user_name = user_data[self.profile_api_user_field] | ||
|
||
|
@@ -214,15 +215,24 @@ def remember(self, user_name): | |
rememberer = self._get_rememberer(environ) | ||
identity = {'repoze.who.userid': user_name} | ||
headers = rememberer.remember(environ, identity) | ||
response = jsonify() | ||
for header, value in headers: | ||
toolkit.response.headers.add(header, value) | ||
response.headers[header] = value | ||
return response | ||
|
||
def redirect_from_callback(self): | ||
def redirect_from_callback(self, resp_remember): | ||
'''Redirect to the callback URL after a successful authentication.''' | ||
state = toolkit.request.params.get('state') | ||
came_from = get_came_from(state) | ||
toolkit.response.status = 302 | ||
toolkit.response.location = came_from | ||
|
||
response = jsonify() | ||
response.status_code = 302 | ||
for header, value in resp_remember.headers: | ||
response.headers[header] = value | ||
response.headers['location'] = came_from | ||
response.autocorrect_location_header = False | ||
return response | ||
|
||
|
||
def get_stored_token(self, user_name): | ||
user_token = db.UserToken.by_user_name(user_name=user_name) | ||
|
@@ -235,8 +245,10 @@ def get_stored_token(self, user_name): | |
} | ||
|
||
def update_token(self, user_name, token): | ||
|
||
user_token = db.UserToken.by_user_name(user_name=user_name) | ||
try: | ||
user_token = db.UserToken.by_user_name(user_name=user_name) | ||
except AttributeError as e: | ||
user_token = None | ||
# Create the user if it does not exist | ||
if not user_token: | ||
user_token = db.UserToken() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LICENSE.txt should not be modified.