diff --git a/wp_oauth_backend/wp_oauth.py b/wp_oauth_backend/wp_oauth.py index 2277849..a6645fb 100644 --- a/wp_oauth_backend/wp_oauth.py +++ b/wp_oauth_backend/wp_oauth.py @@ -4,11 +4,11 @@ written by: Lawrence McDaniel date: oct-2022 -usage: Abstract class implementation of BaseOAuth2 to handle the field - mapping and data converstions between the dict that WP Oauth - returns versus the dict that Open edX actually needs. +usage: subclass of BaseOAuth2 Third Party Authtencation client to + handle the field mapping and data conversions between + the dict that WP Oauth returns versus the dict that Open edX + actually needs. """ -from abc import abstractmethod import json from urllib.parse import urlencode from urllib.request import urlopen @@ -20,85 +20,90 @@ logger = getLogger(__name__) VERBOSE_LOGGING = True class StepwiseMathWPOAuth2(BaseOAuth2): """ - WP OAuth authentication backend customized for Open edX + WP OAuth authentication backend customized for Open edX. + see https://python-social-auth.readthedocs.io/en/latest/backends/implementation.html """ - # private utility function. not part of psa. - def _urlopen(self, url): - return urlopen(url).read().decode("utf-8") - # https://python-social-auth.readthedocs.io/en/latest/configuration/settings.html + def __init__(self, strategy, redirect_uri=None): + super().__init__(strategy, redirect_uri) - @property - def ACCESS_TOKEN_METHOD(self): - return 'POST' - - # require redirect domain to match the original initiating domain. - @property - def SOCIAL_AUTH_SANITIZE_REDIRECTS(self): - return True + if VERBOSE_LOGGING: + logger.info('StepwiseMathWPOAuth2() - ready.') + # This defines the backend name and identifies it during the auth process. + # The name is used in the URLs /login/ and /complete/. + # # This is the string value that will appear in the LMS Django Admin # Third Party Authentication / Provider Configuration (OAuth) # setup page drop-down box titled, "Backend name:", just above # the "Client ID:" and "Client Secret:" fields. - @property - def name(self): - return 'stepwisemath-oauth' + name = 'stepwisemath-oauth' # note: no slash at the end of the base url. Python Social Auth # might clean this up for you, but i'm not 100% certain of that. - @property - def BASE_URL(self): - return "https://stepwisemath.ai" + BASE_URL = "https://stepwisemath.ai" + + # The default key name where the user identification field is defined, it’s + # used in the auth process when some basic user data is returned. This Id + # is stored in the UserSocialAuth.uid field and this, together with the + # UserSocialAuth.provider field, is used to uniquely identify a user association. + ID_KEY = 'id' + + # Flags the backend to enforce email validation during the pipeline + # (if the corresponding pipeline social_core.pipeline.mail.mail_validation was enabled). + REQUIRES_EMAIL_VALIDATION = False + + # Some providers give nothing about the user but some basic data like the + # user Id or an email address. The default scope attribute is used to + # specify a default value for the scope argument to request those extra bits. + DEFAULT_SCOPE = 'basic' + + # Specifying the method type required to retrieve your access token if it’s + # not the default GET request. + ACCESS_TOKEN_METHOD = 'POST' + + # require redirect domain to match the original initiating domain. + SOCIAL_AUTH_SANITIZE_REDIRECTS = True + + # During the auth process some basic user data is returned by the provider + # or retrieved by the user_data() method which usually is used to call + # some API on the provider to retrieve it. This data will be stored in the + # UserSocialAuth.extra_data attribute, but to make it accessible under some + # common names on different providers, this attribute defines a list of + # tuples in the form (name, alias) where name is the key in the user data + # (which should be a dict instance) and alias is the name to store it on extra_data. + EXTRA_DATA = [ + ('id', 'id'), + ('is_superuser', 'is_superuser'), + ('is_staff', 'is_staff'), + ('date_joined', 'date_joined'), + ] # the value of the scope separator is user-defined. Check the # scopes field value for your oauth client in your wordpress host. # the wp-oauth default value for scopes is 'basic' but can be # changed to a list. example 'basic, email, profile'. This # list can be delimited with commas, spaces, whatever. - @property - def SCOPE_SEPARATOR(self): - return "," + SCOPE_SEPARATOR = " " - @property - def base_url(self) -> str: - return self.BASE_URL + # private utility function. not part of psa. + def _urlopen(self, url): + return urlopen(url).read().decode("utf-8") - # override AUTHORIZATION_URL in parent class + # override Python Social Auth default end points # see https://wp-oauth.com/docs/general/endpoints/ @property def AUTHORIZATION_URL(self) -> str: - return f"{self.base_url}/oauth/authorize" + return f"{self.BASE_URL}/oauth/authorize" - # overrides ACCESS_TOKEN_URL from parent class @property - # see https://wp-oauth.com/docs/general/endpoints/ def ACCESS_TOKEN_URL(self) -> str: - return f"{self.base_url}/oauth/token" + return f"{self.BASE_URL}/oauth/token" - # overrides USER_QUERY from parent class - # see https://wp-oauth.com/docs/general/endpoints/ @property def USER_QUERY(self) -> str: - return f"{self.base_url}/oauth/me" + return f"{self.BASE_URL}/oauth/me" - # overrides EXTRA_DATA from parent class - # see https://python-social-auth.readthedocs.io/en/latest/backends/implementation.html - @property - def EXTRA_DATA(self) -> list: - return [ - ('id', 'id'), - ('username', 'username'), - ('email', 'email'), - ('first_name', 'first_name'), - ('last_name', 'last_name'), - ('fullname', 'fullname'), - ('is_superuser', 'is_superuser'), - ('is_staff', 'is_staff'), - ('date_joined', 'date_joined'), - ] - - # implementation of get_user_details() # see https://python-social-auth.readthedocs.io/en/latest/backends/implementation.html def get_user_details(self, response) -> dict: """Return user details from the WP account""" @@ -169,6 +174,5 @@ class StepwiseMathWPOAuth2(BaseOAuth2): user_details = self.get_user_details(response) return user_details except ValueError as e: - logger.error('user_data() did not work: {err}'.format(err=e)) + logger.error('user_data() {err}'.format(err=e)) return None -