From e418438f514504e53ab5e37a88f05fc0f99e5af2 Mon Sep 17 00:00:00 2001 From: lpm0073 Date: Mon, 3 Oct 2022 11:46:35 -0500 Subject: [PATCH] scaffold oauth client --- .gitignore | 82 ++++++++++++++++++++++++ LICENSE.txt | 22 +++++++ MANIFEST.in | 1 + README.rst | 58 +++++++++++++++++ pyproject.toml | 3 + requirements/latest-psa.txt | 3 + requirements/stable-psa.txt | 3 + requirements/test.txt | 5 ++ setup.cfg | 2 + setup.py | 47 ++++++++++++++ stepwisemathai_oauth_backend/__init__.py | 8 +++ stepwisemathai_oauth_backend/wp_oauth.py | 76 ++++++++++++++++++++++ 12 files changed, 310 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 MANIFEST.in create mode 100644 README.rst create mode 100644 pyproject.toml create mode 100644 requirements/latest-psa.txt create mode 100644 requirements/stable-psa.txt create mode 100644 requirements/test.txt create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 stepwisemathai_oauth_backend/__init__.py create mode 100644 stepwisemathai_oauth_backend/wp_oauth.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7e516ab --- /dev/null +++ b/.gitignore @@ -0,0 +1,82 @@ +# Compiled python modules. +*.py[cod] +*.pyc +__pycache__ +.pytest_cache/ + +# C extensions +*.so + +# Python egg metadata, regenerated from source files by setuptools. +# Packages +*.egg +*.egg-info + +# Setuptools distribution folder. +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg +lib +lib64 + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.cache/ +.coverage +.coverage.* +.tox +coverage.xml +htmlcov/ + +# Translations +*.mo + +# IDEs and text editors +*~ +*.swp +.idea/ +.project +.pycharm_helpers/ +.pydevproject + +# The Silver Searcher +.agignore + +# OS X artifacts +*.DS_Store + +# Logging +log/ +logs/ +chromedriver.log +ghostdriver.log + +# Complexity +output/*.html +output/*/index.html + +# Sphinx +docs/_build +docs/modules.rst +docs/trinity_oauth_backend.rst +docs/trinity_oauth_backend.*.rst + +# Private requirements +requirements/private.in +requirements/private.txt + +# tox environment temporary artifacts +tests/__init__.py + +# Development task artifacts +default.db + +.vscode/ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..6fcf6d5 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,22 @@ +The MIT License + + +Copyright (c) 2018 Querium Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..9561fb1 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include README.rst diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..d9aaf5f --- /dev/null +++ b/README.rst @@ -0,0 +1,58 @@ +WP OAuth 2 Backend +============================= + +Overview +-------- + +A Python Social Auth backend for WP OAuth, mostly used for Open edX but can be used elsewhere. +This package was originally cloned from https://github.com/appsembler/trinity-oauth-backend. + +This package is structured so that it can be uploaded to PyPI and installed using pip or easyinstall. +More detail here: https://python-packaging.readthedocs.io/en/latest/minimal.html + +Setup +----- + +General Python/Django +~~~~~~~~~~~~~~~~~~~~~ + +include this repo in your project's requiremets.txt, or install it from the command line. + +.. code-block:: bash + :caption: Python/Django installation + cd path/to/your/project + source path/to/venv/bin/activate + pip install https://github.com/StepwiseMath/stepwisemathai-oauth-backend + +.. code-block:: yaml + :caption: lms.envs.tutor.production.py + ADDL_INSTALLED_APPS: + - "wp_oauth_backend" + THIRD_PARTY_AUTH_BACKENDS: + - "stepwisemathai_oauth_backend.wp_oauth.WPOAuth2" + ENABLE_REQUIRE_THIRD_PARTY_AUTH: true + +add these settings to django.conf: + +.. list-table:: WP Oauth setup + :widths: 50 100 + :header-rows: 1 + + * - Key + - Value + * - WPOAUTH_BACKEND_BASE_URL + - https://stepwisemath.ai + * - WPOAUTH_BACKEND_CLIENT_ID + - see: https://stepwisemath.ai/wp-admin/admin.php?page=wo_manage_clients + * - WPOAUTH_BACKEND_CLIENT_SECRET + - see: https://stepwisemath.ai/wp-admin/admin.php?page=wo_manage_clients + + +Cookiecutter openedx_devops +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +1. include this repository in your Build additional requirements +2. + +Developer Notes +------------- \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..864b334 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta:__legacy__" diff --git a/requirements/latest-psa.txt b/requirements/latest-psa.txt new file mode 100644 index 0000000..0ae72f3 --- /dev/null +++ b/requirements/latest-psa.txt @@ -0,0 +1,3 @@ +# Latest Python Social Auth +social-auth-app-django +social-auth-core diff --git a/requirements/stable-psa.txt b/requirements/stable-psa.txt new file mode 100644 index 0000000..7f07b01 --- /dev/null +++ b/requirements/stable-psa.txt @@ -0,0 +1,3 @@ +# Stable Python Social Auth, found in Open edX Nutmeg +social-auth-app-django==5.0.0 +social-auth-core==4.2.0 \ No newline at end of file diff --git a/requirements/test.txt b/requirements/test.txt new file mode 100644 index 0000000..ce3eb1c --- /dev/null +++ b/requirements/test.txt @@ -0,0 +1,5 @@ +# Packages for testing +pytest==7.1.1 +httpretty==1.1.4 +pycodestyle==2.8.0 +-e . diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..5e40900 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[wheel] +universal = 1 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e55b93a --- /dev/null +++ b/setup.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# pylint: disable=C0111,W6005,W6100 + +import os +import re + +from setuptools import setup + + +def get_version(*file_paths): + """ + Extract the version string from the file at the given relative path. + """ + filename = os.path.join(os.path.dirname(__file__), *file_paths) + version_file = open(filename).read() + version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", + version_file, re.M) + if version_match: + return version_match.group(1) + raise RuntimeError('Unable to find version string.') + + +VERSION = get_version('stepwisemathai_oauth_backend', '__init__.py') + +README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read() + +setup( + name='stepwisemathai', + version=VERSION, + description=('An OAuth backend for the WP OAuth Plugin, ' + 'mostly used for Open edX but can be used elsewhere.'), + long_description=README, + author='Lawrence McDaniel, lpm0073@gmail.com', + author_email='lpm0073@gmail.com', + url='https://github.com/StepwiseMath/stepwisemathai-oauth-backend', + packages=[ + 'stepwisemathai_oauth_backend', + ], + include_package_data=True, + zip_safe=False, + keywords='WP OAuth', + classifiers=[ + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + ], +) diff --git a/stepwisemathai_oauth_backend/__init__.py b/stepwisemathai_oauth_backend/__init__.py new file mode 100644 index 0000000..4587ef4 --- /dev/null +++ b/stepwisemathai_oauth_backend/__init__.py @@ -0,0 +1,8 @@ +""" +An OAuth backend for WordPress. + +It's mostly used for Open edX but can be used elsewhere. +""" + + +__version__ = '0.1.0' diff --git a/stepwisemathai_oauth_backend/wp_oauth.py b/stepwisemathai_oauth_backend/wp_oauth.py new file mode 100644 index 0000000..1c53e88 --- /dev/null +++ b/stepwisemathai_oauth_backend/wp_oauth.py @@ -0,0 +1,76 @@ +import json +from urllib.parse import urlencode +from urllib.request import urlopen +from social_core.backends.oauth import BaseOAuth2 +from django.conf import settings + +from logging import getLogger +logger = getLogger(__name__) + + +class WPOAuth2(BaseOAuth2): + + """WP OAuth authentication backend""" + name = 'wp-oauth2' + SOCIAL_AUTH_SANITIZE_REDIRECTS = False + ACCESS_TOKEN_METHOD = 'POST' + EXTRA_DATA = [] + SCOPE_SEPARATOR = ',' + + @property + def base_url(self): + return settings.WPOAUTH_BACKEND_BASE_URL + + @property + def CLIENT_ID(self): + return settings.WPOAUTH_BACKEND_CLIENT_ID + + @property + def CLIENT_SECRET(self): + return settings.WPOAUTH_BACKEND_CLIENT_SECRET + + @property + def AUTHORIZATION_URL(self) -> str: + return f"{self.base_url}/oauth/authorize" + + @property + def ACCESS_TOKEN_URL(self) -> str: + return f"{self.base_url}/oauth/token" + + @property + def USER_QUERY(self) -> str: + return f"{self.base_url}/oauth/me" + + def get_user_details(self, response): + """Return user details from the WP account""" + user_details = { + 'id': int(response.get('ID')), + 'username': response.get('user_login'), + 'email': response.get('user_email'), + 'fullname': response.get('display_name'), + } + logger.info('get_user_details() - {}'.format(user_details)) + return user_details + + def user_data(self, access_token, *args, **kwargs): + """Loads user data from service""" + url = f'{self.USER_QUERY}?' + urlencode({ + 'access_token': access_token + }) + + try: + return json.loads(self.urlopen(url)) + except ValueError: + return None + + def urlopen(self, url): + return urlopen(url).read().decode("utf-8") + + def get_user_id(self, details, response): + return details['id'] + + def get_username(self, strategy, details, backend, user=None, *args, **kwargs): + return details['username'] + + def get_key_and_secret(self): + return (self.CLIENT_ID, self.CLIENT_SECRET)