lint w black. add more logging
This commit is contained in:
		
							
								
								
									
										7
									
								
								data/wp-oauth-me.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								data/wp-oauth-me.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "access_token": "x39tb1rws4gcpsyvahkofrj7xwnrefxuk3jonrjn",
 | 
				
			||||||
 | 
					    "expires_in": 3600,
 | 
				
			||||||
 | 
					    "refresh_token": "bo85pscgut9nqy56snnzgw6ixljzspac3f74eemy",
 | 
				
			||||||
 | 
					    "scope": "basic profile email",
 | 
				
			||||||
 | 
					    "token_type": "Bearer"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								setup.py
									
									
									
									
									
								
							@@ -11,10 +11,12 @@ os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
HERE = os.path.abspath(os.path.dirname(__file__))
 | 
					HERE = os.path.abspath(os.path.dirname(__file__))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def load_readme():
 | 
					def load_readme():
 | 
				
			||||||
    with io.open(os.path.join(HERE, "README.rst"), "rt", encoding="utf8") as f:
 | 
					    with io.open(os.path.join(HERE, "README.rst"), "rt", encoding="utf8") as f:
 | 
				
			||||||
        return f.read()
 | 
					        return f.read()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def load_about():
 | 
					def load_about():
 | 
				
			||||||
    about = {}
 | 
					    about = {}
 | 
				
			||||||
    with io.open(
 | 
					    with io.open(
 | 
				
			||||||
@@ -25,6 +27,7 @@ def load_about():
 | 
				
			|||||||
        exec(f.read(), about)  # pylint: disable=exec-used
 | 
					        exec(f.read(), about)  # pylint: disable=exec-used
 | 
				
			||||||
    return about
 | 
					    return about
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def load_requirements(*requirements_paths):
 | 
					def load_requirements(*requirements_paths):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Load all requirements from the specified requirements files.
 | 
					    Load all requirements from the specified requirements files.
 | 
				
			||||||
@@ -34,7 +37,9 @@ def load_requirements(*requirements_paths):
 | 
				
			|||||||
    requirements = set()
 | 
					    requirements = set()
 | 
				
			||||||
    for path in requirements_paths:
 | 
					    for path in requirements_paths:
 | 
				
			||||||
        requirements.update(
 | 
					        requirements.update(
 | 
				
			||||||
            line.split("#")[0].strip() for line in open(path).readlines() if is_requirement(line.strip())
 | 
					            line.split("#")[0].strip()
 | 
				
			||||||
 | 
					            for line in open(path).readlines()
 | 
				
			||||||
 | 
					            if is_requirement(line.strip())
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    return list(requirements)
 | 
					    return list(requirements)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -54,19 +59,22 @@ def is_requirement(line):
 | 
				
			|||||||
        or line.startswith("git+")
 | 
					        or line.startswith("git+")
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
README = load_readme()
 | 
					README = load_readme()
 | 
				
			||||||
ABOUT = load_about()
 | 
					ABOUT = load_about()
 | 
				
			||||||
VERSION = ABOUT["__version__"]
 | 
					VERSION = ABOUT["__version__"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
setup(
 | 
					setup(
 | 
				
			||||||
    name='wp-oauth-backend',
 | 
					    name="wp-oauth-backend",
 | 
				
			||||||
    version=VERSION,
 | 
					    version=VERSION,
 | 
				
			||||||
    description=('An OAuth backend for the WP OAuth Wordpress Plugin, '
 | 
					    description=(
 | 
				
			||||||
                 'that is customized for use in Open edX installations.'),
 | 
					        "An OAuth backend for the WP OAuth Wordpress Plugin, "
 | 
				
			||||||
 | 
					        "that is customized for use in Open edX installations."
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
    long_description=README,
 | 
					    long_description=README,
 | 
				
			||||||
    author='Lawrence McDaniel, lpm0073@gmail.com',
 | 
					    author="Lawrence McDaniel, lpm0073@gmail.com",
 | 
				
			||||||
    author_email='lpm0073@gmail.com',
 | 
					    author_email="lpm0073@gmail.com",
 | 
				
			||||||
    url='https://github.com/StepwiseMath/wp-oauth-backend',
 | 
					    url="https://github.com/StepwiseMath/wp-oauth-backend",
 | 
				
			||||||
    project_urls={
 | 
					    project_urls={
 | 
				
			||||||
        "Code": "https://github.com/StepwiseMath/wp-oauth-backend",
 | 
					        "Code": "https://github.com/StepwiseMath/wp-oauth-backend",
 | 
				
			||||||
        "Issue tracker": "https://github.com/StepwiseMath/wp-oauth-backend/issues",
 | 
					        "Issue tracker": "https://github.com/StepwiseMath/wp-oauth-backend/issues",
 | 
				
			||||||
@@ -76,11 +84,11 @@ setup(
 | 
				
			|||||||
    include_package_data=True,
 | 
					    include_package_data=True,
 | 
				
			||||||
    package_data={"": ["*.html"]},  # include any templates found in this repo.
 | 
					    package_data={"": ["*.html"]},  # include any templates found in this repo.
 | 
				
			||||||
    zip_safe=False,
 | 
					    zip_safe=False,
 | 
				
			||||||
    keywords='WP OAuth',
 | 
					    keywords="WP OAuth",
 | 
				
			||||||
    python_requires=">=3.7",
 | 
					    python_requires=">=3.7",
 | 
				
			||||||
    install_requires=load_requirements("requirements/stable-psa.txt"),
 | 
					    install_requires=load_requirements("requirements/stable-psa.txt"),
 | 
				
			||||||
    classifiers=[
 | 
					    classifiers=[
 | 
				
			||||||
        'Intended Audience :: Developers',
 | 
					        "Intended Audience :: Developers",
 | 
				
			||||||
        'License :: OSI Approved :: MIT License',
 | 
					        "License :: OSI Approved :: MIT License",
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +1 @@
 | 
				
			|||||||
__version__ = '0.1.0'
 | 
					__version__ = "0.1.0"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,8 @@ User = get_user_model()
 | 
				
			|||||||
logger = getLogger(__name__)
 | 
					logger = getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VERBOSE_LOGGING = True
 | 
					VERBOSE_LOGGING = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StepwiseMathWPOAuth2(BaseOAuth2):
 | 
					class StepwiseMathWPOAuth2(BaseOAuth2):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    WP OAuth authentication backend customized for Open edX.
 | 
					    WP OAuth authentication backend customized for Open edX.
 | 
				
			||||||
@@ -37,6 +39,7 @@ class StepwiseMathWPOAuth2(BaseOAuth2):
 | 
				
			|||||||
      upstream to break. If you try this then you'll get an error about a missing
 | 
					      upstream to break. If you try this then you'll get an error about a missing
 | 
				
			||||||
      positional argument, 'strategy'.
 | 
					      positional argument, 'strategy'.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _user_details = None
 | 
					    _user_details = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # This defines the backend name and identifies it during the auth process.
 | 
					    # This defines the backend name and identifies it during the auth process.
 | 
				
			||||||
@@ -46,7 +49,7 @@ class StepwiseMathWPOAuth2(BaseOAuth2):
 | 
				
			|||||||
    # Third Party Authentication / Provider Configuration (OAuth)
 | 
					    # Third Party Authentication / Provider Configuration (OAuth)
 | 
				
			||||||
    # setup page drop-down box titled, "Backend name:", just above
 | 
					    # setup page drop-down box titled, "Backend name:", just above
 | 
				
			||||||
    # the "Client ID:" and "Client Secret:" fields.
 | 
					    # the "Client ID:" and "Client Secret:" fields.
 | 
				
			||||||
    name = 'stepwisemath-oauth'
 | 
					    name = "stepwisemath-oauth"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # note: no slash at the end of the base url. Python Social Auth
 | 
					    # 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.
 | 
					    # might clean this up for you, but i'm not 100% certain of that.
 | 
				
			||||||
@@ -56,7 +59,7 @@ class StepwiseMathWPOAuth2(BaseOAuth2):
 | 
				
			|||||||
    # used in the auth process when some basic user data is returned. This Id
 | 
					    # 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
 | 
					    # is stored in the UserSocialAuth.uid field and this, together with the
 | 
				
			||||||
    # UserSocialAuth.provider field, is used to uniquely identify a user association.
 | 
					    # UserSocialAuth.provider field, is used to uniquely identify a user association.
 | 
				
			||||||
    ID_KEY = 'id'
 | 
					    ID_KEY = "id"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Flags the backend to enforce email validation during the pipeline
 | 
					    # Flags the backend to enforce email validation during the pipeline
 | 
				
			||||||
    # (if the corresponding pipeline social_core.pipeline.mail.mail_validation was enabled).
 | 
					    # (if the corresponding pipeline social_core.pipeline.mail.mail_validation was enabled).
 | 
				
			||||||
@@ -69,11 +72,11 @@ class StepwiseMathWPOAuth2(BaseOAuth2):
 | 
				
			|||||||
    # wp-oauth supports 4 scopes: basic, email, profile, openeid.
 | 
					    # wp-oauth supports 4 scopes: basic, email, profile, openeid.
 | 
				
			||||||
    # we want the first three of these.
 | 
					    # we want the first three of these.
 | 
				
			||||||
    # see https://wp-oauth.com/docs/how-to/adding-supported-scopes/
 | 
					    # see https://wp-oauth.com/docs/how-to/adding-supported-scopes/
 | 
				
			||||||
    DEFAULT_SCOPE = ['basic', 'profile', 'email']
 | 
					    DEFAULT_SCOPE = ["basic", "profile", "email"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Specifying the method type required to retrieve your access token if it’s
 | 
					    # Specifying the method type required to retrieve your access token if it’s
 | 
				
			||||||
    # not the default GET request.
 | 
					    # not the default GET request.
 | 
				
			||||||
    ACCESS_TOKEN_METHOD = 'POST'    
 | 
					    ACCESS_TOKEN_METHOD = "POST"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # require redirect domain to match the original initiating domain.
 | 
					    # require redirect domain to match the original initiating domain.
 | 
				
			||||||
    SOCIAL_AUTH_SANITIZE_REDIRECTS = True
 | 
					    SOCIAL_AUTH_SANITIZE_REDIRECTS = True
 | 
				
			||||||
@@ -86,11 +89,11 @@ class StepwiseMathWPOAuth2(BaseOAuth2):
 | 
				
			|||||||
    # tuples in the form (name, alias) where name is the key in the user data
 | 
					    # 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.
 | 
					    # (which should be a dict instance) and alias is the name to store it on extra_data.
 | 
				
			||||||
    EXTRA_DATA = [
 | 
					    EXTRA_DATA = [
 | 
				
			||||||
            ('id', 'id'),
 | 
					        ("id", "id"),
 | 
				
			||||||
            ('is_superuser', 'is_superuser'),
 | 
					        ("is_superuser", "is_superuser"),
 | 
				
			||||||
            ('is_staff', 'is_staff'),
 | 
					        ("is_staff", "is_staff"),
 | 
				
			||||||
            ('date_joined', 'date_joined'),
 | 
					        ("date_joined", "date_joined"),
 | 
				
			||||||
        ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # the value of the scope separator is user-defined. Check the
 | 
					    # the value of the scope separator is user-defined. Check the
 | 
				
			||||||
    # scopes field value for your oauth client in your wordpress host.
 | 
					    # scopes field value for your oauth client in your wordpress host.
 | 
				
			||||||
@@ -112,12 +115,25 @@ class StepwiseMathWPOAuth2(BaseOAuth2):
 | 
				
			|||||||
        in qc_keys.
 | 
					        in qc_keys.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if not type(response) == dict:
 | 
					        if not type(response) == dict:
 | 
				
			||||||
            logger.warning('is_valid_user_details() was expecting a dict but received an object of type: {type}'.format(
 | 
					            logger.warning(
 | 
				
			||||||
                type=type(response)
 | 
					                "is_valid_user_details() was expecting a dict but received an object of type: {type}".format(
 | 
				
			||||||
            ))
 | 
					                    type=type(response)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
        qc_keys = ['id', 'date_joined', 'email', 'first_name', 'fullname', 'is_staff', 'is_superuser', 'last_name', 'username']
 | 
					        qc_keys = [
 | 
				
			||||||
        if all(key in response for key in qc_keys): return True
 | 
					            "id",
 | 
				
			||||||
 | 
					            "date_joined",
 | 
				
			||||||
 | 
					            "email",
 | 
				
			||||||
 | 
					            "first_name",
 | 
				
			||||||
 | 
					            "fullname",
 | 
				
			||||||
 | 
					            "is_staff",
 | 
				
			||||||
 | 
					            "is_superuser",
 | 
				
			||||||
 | 
					            "last_name",
 | 
				
			||||||
 | 
					            "username",
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        if all(key in response for key in qc_keys):
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
        return False
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_wp_oauth_response(self, response) -> bool:
 | 
					    def is_wp_oauth_response(self, response) -> bool:
 | 
				
			||||||
@@ -126,22 +142,39 @@ class StepwiseMathWPOAuth2(BaseOAuth2):
 | 
				
			|||||||
        supposed to be a dict with at least the keys included in qc_keys.
 | 
					        supposed to be a dict with at least the keys included in qc_keys.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if not type(response) == dict:
 | 
					        if not type(response) == dict:
 | 
				
			||||||
            logger.warning('is_valid_user_details() was expecting a dict but received an object of type: {type}'.format(
 | 
					            logger.warning(
 | 
				
			||||||
                type=type(response)
 | 
					                "is_valid_user_details() was expecting a dict but received an object of type: {type}".format(
 | 
				
			||||||
            ))
 | 
					                    type=type(response)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
        qc_keys = ['ID' 'display_name', 'user_email', 'user_login', 'user_roles']
 | 
					        qc_keys = ["ID" "display_name", "user_email", "user_login", "user_roles"]
 | 
				
			||||||
        if all(key in response for key in qc_keys): return True
 | 
					        if all(key in response for key in qc_keys):
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
        return False
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_wp_oauth_extended_response(self, response) -> bool:
 | 
					    def is_wp_oauth_refresh_token_response(self, response) -> bool:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        validate the structure of the extended response object from wp-oauth. it's 
 | 
					        validate that the structure of the response contains the keys of
 | 
				
			||||||
        supposed to be a dict with at least the keys included in qc_keys.
 | 
					        a refresh token dict.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if not self.is_valid_user_details(response): return False
 | 
					        if not self.is_valid_user_details(response):
 | 
				
			||||||
        qc_keys = ['access_token' 'expires_in', 'refresh_token', 'scope', 'token_type']
 | 
					            return False
 | 
				
			||||||
        if all(key in response for key in qc_keys): return True
 | 
					        qc_keys = ["access_token" "expires_in", "refresh_token", "scope", "token_type"]
 | 
				
			||||||
 | 
					        if all(key in response for key in qc_keys):
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def is_get_user_details_extended_dict(self, response) -> bool:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        validate whether the structure the response is a dict that
 | 
				
			||||||
 | 
					        contains a.) all keys of a get_user_details() return, plus,
 | 
				
			||||||
 | 
					        b.) all keys of a wp-oauth refresh token response.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if not self.is_valid_user_details(response):
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        if self.is_wp_oauth_refresh_token_response(response):
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
        return False
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # override Python Social Auth default end points.
 | 
					    # override Python Social Auth default end points.
 | 
				
			||||||
@@ -153,21 +186,21 @@ class StepwiseMathWPOAuth2(BaseOAuth2):
 | 
				
			|||||||
    def AUTHORIZATION_URL(self) -> str:
 | 
					    def AUTHORIZATION_URL(self) -> str:
 | 
				
			||||||
        retval = f"{self.BASE_URL}/oauth/authorize"
 | 
					        retval = f"{self.BASE_URL}/oauth/authorize"
 | 
				
			||||||
        if VERBOSE_LOGGING:
 | 
					        if VERBOSE_LOGGING:
 | 
				
			||||||
            logger.info('AUTHORIZATION_URL: {url}'.format(url=retval))
 | 
					            logger.info("AUTHORIZATION_URL: {url}".format(url=retval))
 | 
				
			||||||
        return retval
 | 
					        return retval
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def ACCESS_TOKEN_URL(self) -> str:
 | 
					    def ACCESS_TOKEN_URL(self) -> str:
 | 
				
			||||||
        retval = f"{self.BASE_URL}/oauth/token"
 | 
					        retval = f"{self.BASE_URL}/oauth/token"
 | 
				
			||||||
        if VERBOSE_LOGGING:
 | 
					        if VERBOSE_LOGGING:
 | 
				
			||||||
            logger.info('ACCESS_TOKEN_URL: {url}'.format(url=retval))
 | 
					            logger.info("ACCESS_TOKEN_URL: {url}".format(url=retval))
 | 
				
			||||||
        return retval
 | 
					        return retval
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def USER_QUERY(self) -> str:
 | 
					    def USER_QUERY(self) -> str:
 | 
				
			||||||
        retval = f"{self.BASE_URL}/oauth/me"
 | 
					        retval = f"{self.BASE_URL}/oauth/me"
 | 
				
			||||||
        if VERBOSE_LOGGING:
 | 
					        if VERBOSE_LOGGING:
 | 
				
			||||||
            logger.info('USER_QUERY: {url}'.format(url=retval))
 | 
					            logger.info("USER_QUERY: {url}".format(url=retval))
 | 
				
			||||||
        return retval
 | 
					        return retval
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
@@ -178,28 +211,39 @@ class StepwiseMathWPOAuth2(BaseOAuth2):
 | 
				
			|||||||
    def user_details(self, value: dict):
 | 
					    def user_details(self, value: dict):
 | 
				
			||||||
        if self.is_valid_user_details(value):
 | 
					        if self.is_valid_user_details(value):
 | 
				
			||||||
            if VERBOSE_LOGGING:
 | 
					            if VERBOSE_LOGGING:
 | 
				
			||||||
                logger.info('user_details.setter: new value set {value}'.format(
 | 
					                logger.info(
 | 
				
			||||||
                    value=json.dumps(value, sort_keys=True, indent=4)
 | 
					                    "user_details.setter: new value set {value}".format(
 | 
				
			||||||
                ))
 | 
					                        value=json.dumps(value, sort_keys=True, indent=4)
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            self._user_details = value
 | 
					            self._user_details = value
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            logger.error('user_details.setter: tried to pass an invalid object {value}'.format(
 | 
					            logger.error(
 | 
				
			||||||
                value=json.dumps(value, sort_keys=True, indent=4)
 | 
					                "user_details.setter: tried to pass an invalid object {value}".format(
 | 
				
			||||||
            ))
 | 
					                    value=json.dumps(value, sort_keys=True, indent=4)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # see https://python-social-auth.readthedocs.io/en/latest/backends/implementation.html
 | 
					    # see https://python-social-auth.readthedocs.io/en/latest/backends/implementation.html
 | 
				
			||||||
    # Return user details from the Wordpress user account
 | 
					    # Return user details from the Wordpress user account
 | 
				
			||||||
    def get_user_details(self, response) -> dict:
 | 
					    def get_user_details(self, response) -> dict:
 | 
				
			||||||
        if not (self.is_valid_user_details(response) or self.is_wp_oauth_response(response)):
 | 
					        if not (
 | 
				
			||||||
            logger.error('get_user_details() -  received an unrecognized response object. Cannot continue: {response}'.format(
 | 
					            self.is_valid_user_details(response) or self.is_wp_oauth_response(response)
 | 
				
			||||||
                response=json.dumps(response, sort_keys=True, indent=4)
 | 
					        ):
 | 
				
			||||||
                ))
 | 
					            logger.error(
 | 
				
			||||||
 | 
					                "get_user_details() -  received an unrecognized response object. Cannot continue: {response}".format(
 | 
				
			||||||
 | 
					                    response=json.dumps(response, sort_keys=True, indent=4)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            # if we have cached results then we might be able to recover.
 | 
					            # if we have cached results then we might be able to recover.
 | 
				
			||||||
            return self.user_details
 | 
					            return self.user_details
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if VERBOSE_LOGGING: logger.info('get_user_details() begin with response: {response}'.format(
 | 
					        if VERBOSE_LOGGING:
 | 
				
			||||||
            response=json.dumps(response, sort_keys=True, indent=4)
 | 
					            logger.info(
 | 
				
			||||||
        ))
 | 
					                "get_user_details() begin with response: {response}".format(
 | 
				
			||||||
 | 
					                    response=json.dumps(response, sort_keys=True, indent=4)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
        # a def in the third_party_auth pipeline list calls get_user_details() after its already
 | 
					        # a def in the third_party_auth pipeline list calls get_user_details() after its already
 | 
				
			||||||
        # been called once. i don't know why. but, it passes the original get_user_details() dict
 | 
					        # been called once. i don't know why. but, it passes the original get_user_details() dict
 | 
				
			||||||
        # enhanced with additional token-related keys. if we receive this modified dict then we
 | 
					        # enhanced with additional token-related keys. if we receive this modified dict then we
 | 
				
			||||||
@@ -207,64 +251,72 @@ class StepwiseMathWPOAuth2(BaseOAuth2):
 | 
				
			|||||||
        #
 | 
					        #
 | 
				
			||||||
        # If most of the original keys (see dict definition below) exist in the response object
 | 
					        # If most of the original keys (see dict definition below) exist in the response object
 | 
				
			||||||
        # then we can assume that this is our case.
 | 
					        # then we can assume that this is our case.
 | 
				
			||||||
        if self.is_wp_oauth_extended_response(response):
 | 
					        if self.is_get_user_details_extended_dict(response):
 | 
				
			||||||
            # -------------------------------------------------------------
 | 
					            # -------------------------------------------------------------
 | 
				
			||||||
            # expected use case #2: an enhanced derivation of an original
 | 
					            # expected use case #2: an enhanced derivation of an original
 | 
				
			||||||
            # user_details dict. This is created when get_user_details()
 | 
					            # user_details dict. This is created when get_user_details()
 | 
				
			||||||
            # is called from user_data().
 | 
					            # is called from user_data().
 | 
				
			||||||
            # -------------------------------------------------------------
 | 
					            # -------------------------------------------------------------
 | 
				
			||||||
            if VERBOSE_LOGGING:
 | 
					            if VERBOSE_LOGGING:
 | 
				
			||||||
                logger.info('get_user_details() -  detected an enhanced get_user_details() dict in the response: {response}'.format(
 | 
					                logger.info(
 | 
				
			||||||
                    response=json.dumps(response, sort_keys=True, indent=4)
 | 
					                    "get_user_details() -  detected an enhanced get_user_details() dict in the response: {response}".format(
 | 
				
			||||||
                    ))
 | 
					                        response=json.dumps(response, sort_keys=True, indent=4)
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            return response
 | 
					            return response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # at this point we've ruled out the possibility of the response object
 | 
					        # at this point we've ruled out the possibility of the response object
 | 
				
			||||||
        # being a derivation of a user_details dict. So, it should therefore
 | 
					        # being a derivation of a user_details dict. So, it should therefore
 | 
				
			||||||
        # conform to the structure of a wp-oauth dict.
 | 
					        # conform to the structure of a wp-oauth dict.
 | 
				
			||||||
        if not self.is_wp_oauth_response(response):
 | 
					        if not self.is_wp_oauth_response(response):
 | 
				
			||||||
            logger.warning('get_user_details() -  response object is not a valid wp-oauth object. Cannot continue. {response}'.format(
 | 
					            logger.warning(
 | 
				
			||||||
                response=json.dumps(response, sort_keys=True, indent=4)
 | 
					                "get_user_details() -  response object is not a valid wp-oauth object. Cannot continue. {response}".format(
 | 
				
			||||||
            ))
 | 
					                    response=json.dumps(response, sort_keys=True, indent=4)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            return self.user_details
 | 
					            return self.user_details
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # -------------------------------------------------------------
 | 
					        # -------------------------------------------------------------
 | 
				
			||||||
        # expected use case #1: response object is a dict with all required keys.
 | 
					        # expected use case #1: response object is a dict with all required keys.
 | 
				
			||||||
        # -------------------------------------------------------------
 | 
					        # -------------------------------------------------------------
 | 
				
			||||||
        if VERBOSE_LOGGING:
 | 
					        if VERBOSE_LOGGING:
 | 
				
			||||||
            logger.info('get_user_details() -  start. response: {response}'.format(
 | 
					            logger.info(
 | 
				
			||||||
                response=json.dumps(response, sort_keys=True, indent=4)
 | 
					                "get_user_details() -  start. response: {response}".format(
 | 
				
			||||||
                ))
 | 
					                    response=json.dumps(response, sort_keys=True, indent=4)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # try to parse out the first and last names
 | 
					        # try to parse out the first and last names
 | 
				
			||||||
        split_name = response.get('display_name', '').split()
 | 
					        split_name = response.get("display_name", "").split()
 | 
				
			||||||
        first_name = split_name[0] if len(split_name) > 0 else ''
 | 
					        first_name = split_name[0] if len(split_name) > 0 else ""
 | 
				
			||||||
        last_name = split_name[-1] if len(split_name) == 2 else ''
 | 
					        last_name = split_name[-1] if len(split_name) == 2 else ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # check for superuser / staff status
 | 
					        # check for superuser / staff status
 | 
				
			||||||
        user_roles = response.get('user_roles', [])        
 | 
					        user_roles = response.get("user_roles", [])
 | 
				
			||||||
        super_user = 'administrator' in user_roles
 | 
					        super_user = "administrator" in user_roles
 | 
				
			||||||
        is_staff = 'administrator' in user_roles
 | 
					        is_staff = "administrator" in user_roles
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.user_details = {
 | 
					        self.user_details = {
 | 
				
			||||||
            'id': int(response.get('ID'), 0),
 | 
					            "id": int(response.get("ID"), 0),
 | 
				
			||||||
            'username': response.get('user_login', ''),
 | 
					            "username": response.get("user_login", ""),
 | 
				
			||||||
            'email': response.get('user_email', ''),
 | 
					            "email": response.get("user_email", ""),
 | 
				
			||||||
            'first_name': first_name,
 | 
					            "first_name": first_name,
 | 
				
			||||||
            'last_name': last_name,
 | 
					            "last_name": last_name,
 | 
				
			||||||
            'fullname': response.get('display_name', ''),
 | 
					            "fullname": response.get("display_name", ""),
 | 
				
			||||||
            'is_superuser': super_user,
 | 
					            "is_superuser": super_user,
 | 
				
			||||||
            'is_staff': is_staff,
 | 
					            "is_staff": is_staff,
 | 
				
			||||||
            'refresh_token': response.get('refresh_token', ''),
 | 
					            "refresh_token": response.get("refresh_token", ""),
 | 
				
			||||||
            'scope': response.get('scope', ''),
 | 
					            "scope": response.get("scope", ""),
 | 
				
			||||||
            'token_type': response.get('token_type', ''),
 | 
					            "token_type": response.get("token_type", ""),
 | 
				
			||||||
            'date_joined': response.get('user_registered', ''),
 | 
					            "date_joined": response.get("user_registered", ""),
 | 
				
			||||||
            'user_status': response.get('user_status', ''),
 | 
					            "user_status": response.get("user_status", ""),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if VERBOSE_LOGGING:
 | 
					        if VERBOSE_LOGGING:
 | 
				
			||||||
            logger.info('get_user_details() -  finish. user_details: {user_details}'.format(
 | 
					            logger.info(
 | 
				
			||||||
                user_details=json.dumps(self.user_details, sort_keys=True, indent=4)
 | 
					                "get_user_details() -  finish. user_details: {user_details}".format(
 | 
				
			||||||
                ))
 | 
					                    user_details=json.dumps(self.user_details, sort_keys=True, indent=4)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
        return self.user_details
 | 
					        return self.user_details
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Load user data from service url end point. Note that in the case of
 | 
					    # Load user data from service url end point. Note that in the case of
 | 
				
			||||||
@@ -273,50 +325,82 @@ class StepwiseMathWPOAuth2(BaseOAuth2):
 | 
				
			|||||||
    #
 | 
					    #
 | 
				
			||||||
    # see https://python-social-auth.readthedocs.io/en/latest/backends/implementation.html
 | 
					    # see https://python-social-auth.readthedocs.io/en/latest/backends/implementation.html
 | 
				
			||||||
    def user_data(self, access_token, *args, **kwargs) -> dict:
 | 
					    def user_data(self, access_token, *args, **kwargs) -> dict:
 | 
				
			||||||
 | 
					        response = None
 | 
				
			||||||
        url = f'{self.USER_QUERY}?' + urlencode({
 | 
					        user_details = None
 | 
				
			||||||
            'access_token': access_token
 | 
					        url = f"{self.USER_QUERY}?" + urlencode({"access_token": access_token})
 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if VERBOSE_LOGGING:
 | 
					        if VERBOSE_LOGGING:
 | 
				
			||||||
            logger.info("user_data() url: {url}".format(url=url))
 | 
					            logger.info("user_data() url: {url}".format(url=url))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            response = json.loads(self._urlopen(url))
 | 
					            response = json.loads(self._urlopen(url))
 | 
				
			||||||
 | 
					            if VERBOSE_LOGGING:
 | 
				
			||||||
 | 
					                logger.info(
 | 
				
			||||||
 | 
					                    "user_data() response: {response}".format(
 | 
				
			||||||
 | 
					                        response=json.dumps(self.user_details, sort_keys=True, indent=4)
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            user_details = self.get_user_details(response)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if VERBOSE_LOGGING:
 | 
				
			||||||
 | 
					                logger.info(
 | 
				
			||||||
 | 
					                    "user_data() local variable user_details: {user_details}".format(
 | 
				
			||||||
 | 
					                        user_details=json.dumps(user_details, sort_keys=True, indent=4)
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            if VERBOSE_LOGGING:
 | 
				
			||||||
 | 
					                logger.info(
 | 
				
			||||||
 | 
					                    "user_data() class property value of self.user_details: {user_details}".format(
 | 
				
			||||||
 | 
					                        user_details=json.dumps(
 | 
				
			||||||
 | 
					                            self.user_details, sort_keys=True, indent=4
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
        except ValueError as e:
 | 
					        except ValueError as e:
 | 
				
			||||||
            logger.error('user_data() {err}'.format(err=e))
 | 
					            logger.error("user_data() {err}".format(err=e))
 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not self.is_valid_user_details(response):
 | 
					        if not self.is_valid_user_details(user_details):
 | 
				
			||||||
            logger.error('user_data() response object is invalid: {response}'.format(
 | 
					            logger.error(
 | 
				
			||||||
                response=json.dumps(self.user_details, sort_keys=True, indent=4)
 | 
					                "user_data() user_details object is invalid: {user_details}".format(
 | 
				
			||||||
            ))
 | 
					                    user_details=json.dumps(user_details, sort_keys=True, indent=4)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            return self.user_details
 | 
					            return self.user_details
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # refresh our internal user_details property after having validated
 | 
					 | 
				
			||||||
        # response from USER_QUERY
 | 
					 | 
				
			||||||
        self.get_user_details(response)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # add syncronization of any data fields that get missed by the built-in
 | 
					        # add syncronization of any data fields that get missed by the built-in
 | 
				
			||||||
        # open edx third party authentication sync functionality.
 | 
					        # open edx third party authentication sync functionality.
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            # this gets called just prior to account creation for
 | 
					            # this gets called just prior to account creation for
 | 
				
			||||||
            # new users, hence, we need to catch DoesNotExist
 | 
					            # new users, hence, we need to catch DoesNotExist
 | 
				
			||||||
            # exceptions.
 | 
					            # exceptions.
 | 
				
			||||||
            user=User.objects.get(username=self.user_details['username'])
 | 
					            user = User.objects.get(username=self.user_details["username"])
 | 
				
			||||||
        except User.DoesNotExist:
 | 
					        except User.DoesNotExist:
 | 
				
			||||||
            return self.user_details
 | 
					            return self.user_details
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (user.is_superuser != self.user_details['is_superuser']) or (user.is_staff != self.user_details['is_staff']):
 | 
					        if (user.is_superuser != self.user_details["is_superuser"]) or (
 | 
				
			||||||
            user.is_superuser = self.user_details['is_superuser']
 | 
					            user.is_staff != self.user_details["is_staff"]
 | 
				
			||||||
            user.is_staff = self.user_details['is_staff']
 | 
					        ):
 | 
				
			||||||
 | 
					            user.is_superuser = self.user_details["is_superuser"]
 | 
				
			||||||
 | 
					            user.is_staff = self.user_details["is_staff"]
 | 
				
			||||||
            user.save()
 | 
					            user.save()
 | 
				
			||||||
            logger.info('Updated the is_superuser/is_staff flags for user {username}'.format(username=user.username))
 | 
					            logger.info(
 | 
				
			||||||
 | 
					                "Updated the is_superuser/is_staff flags for user {username}".format(
 | 
				
			||||||
 | 
					                    username=user.username
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (user.first_name != self.user_details['first_name']) or (user.last_name != self.user_details['last_name']):
 | 
					        if (user.first_name != self.user_details["first_name"]) or (
 | 
				
			||||||
            user.first_name = self.user_details['first_name']
 | 
					            user.last_name != self.user_details["last_name"]
 | 
				
			||||||
            user.last_name = self.user_details['last_name']
 | 
					        ):
 | 
				
			||||||
 | 
					            user.first_name = self.user_details["first_name"]
 | 
				
			||||||
 | 
					            user.last_name = self.user_details["last_name"]
 | 
				
			||||||
            user.save()
 | 
					            user.save()
 | 
				
			||||||
            logger.info('Updated first_name/last_name for user {username}'.format(username=user.username))
 | 
					            logger.info(
 | 
				
			||||||
 | 
					                "Updated first_name/last_name for user {username}".format(
 | 
				
			||||||
 | 
					                    username=user.username
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return self.user_details
 | 
					        return self.user_details
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user