Browse Source

Updated comments in cura/OAuth2

Used DOX_2_RST.py to convert doxygen style comments to sphinx style comments
jelle Spijker 4 years ago
parent
commit
b032101e55

+ 41 - 22
cura/OAuth2/AuthorizationHelpers.py

@@ -16,23 +16,27 @@ from cura.OAuth2.Models import AuthenticationResponse, UserProfile, OAuth2Settin
 catalog = i18nCatalog("cura")
 TOKEN_TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S"
 
-##  Class containing several helpers to deal with the authorization flow.
 class AuthorizationHelpers:
+    """Class containing several helpers to deal with the authorization flow."""
+
     def __init__(self, settings: "OAuth2Settings") -> None:
         self._settings = settings
         self._token_url = "{}/token".format(self._settings.OAUTH_SERVER_URL)
 
     @property
-    ##  The OAuth2 settings object.
     def settings(self) -> "OAuth2Settings":
+        """The OAuth2 settings object."""
+
         return self._settings
 
-    ##  Request the access token from the authorization server.
-    #   \param authorization_code: The authorization code from the 1st step.
-    #   \param verification_code: The verification code needed for the PKCE
-    #   extension.
-    #   \return An AuthenticationResponse object.
     def getAccessTokenUsingAuthorizationCode(self, authorization_code: str, verification_code: str) -> "AuthenticationResponse":
+        """Request the access token from the authorization server.
+        
+        :param authorization_code: The authorization code from the 1st step.
+        :param verification_code: The verification code needed for the PKCE extension.
+        :return: An AuthenticationResponse object.
+        """
+        
         data = {
             "client_id": self._settings.CLIENT_ID if self._settings.CLIENT_ID is not None else "",
             "redirect_uri": self._settings.CALLBACK_URL if self._settings.CALLBACK_URL is not None else "",
@@ -46,10 +50,13 @@ class AuthorizationHelpers:
         except requests.exceptions.ConnectionError:
             return AuthenticationResponse(success=False, err_message="Unable to connect to remote server")
 
-    ##  Request the access token from the authorization server using a refresh token.
-    #   \param refresh_token:
-    #   \return An AuthenticationResponse object.
     def getAccessTokenUsingRefreshToken(self, refresh_token: str) -> "AuthenticationResponse":
+        """Request the access token from the authorization server using a refresh token.
+        
+        :param refresh_token:
+        :return: An AuthenticationResponse object.
+        """
+        
         Logger.log("d", "Refreshing the access token.")
         data = {
             "client_id": self._settings.CLIENT_ID if self._settings.CLIENT_ID is not None else "",
@@ -64,10 +71,13 @@ class AuthorizationHelpers:
             return AuthenticationResponse(success=False, err_message="Unable to connect to remote server")
 
     @staticmethod
-    ##  Parse the token response from the authorization server into an AuthenticationResponse object.
-    #   \param token_response: The JSON string data response from the authorization server.
-    #   \return An AuthenticationResponse object.
     def parseTokenResponse(token_response: requests.models.Response) -> "AuthenticationResponse":
+        """Parse the token response from the authorization server into an AuthenticationResponse object.
+        
+        :param token_response: The JSON string data response from the authorization server.
+        :return: An AuthenticationResponse object.
+        """
+        
         token_data = None
 
         try:
@@ -89,10 +99,13 @@ class AuthorizationHelpers:
                                       scope=token_data["scope"],
                                       received_at=datetime.now().strftime(TOKEN_TIMESTAMP_FORMAT))
 
-    ##  Calls the authentication API endpoint to get the token data.
-    #   \param access_token: The encoded JWT token.
-    #   \return Dict containing some profile data.
     def parseJWT(self, access_token: str) -> Optional["UserProfile"]:
+        """Calls the authentication API endpoint to get the token data.
+        
+        :param access_token: The encoded JWT token.
+        :return: Dict containing some profile data.
+        """
+        
         try:
             token_request = requests.get("{}/check-token".format(self._settings.OAUTH_SERVER_URL), headers = {
                 "Authorization": "Bearer {}".format(access_token)
@@ -115,16 +128,22 @@ class AuthorizationHelpers:
         )
 
     @staticmethod
-    ##  Generate a verification code of arbitrary length.
-    #   \param code_length: How long should the code be? This should never be lower than 16, but it's probably better to
-    #   leave it at 32
     def generateVerificationCode(code_length: int = 32) -> str:
+        """Generate a verification code of arbitrary length.
+        
+        :param code_length:: How long should the code be? This should never be lower than 16, but it's probably
+        better to leave it at 32
+        """
+        
         return "".join(random.choice("0123456789ABCDEF") for i in range(code_length))
 
     @staticmethod
-    ##  Generates a base64 encoded sha512 encrypted version of a given string.
-    #   \param verification_code:
-    #   \return The encrypted code in base64 format.
     def generateVerificationCodeChallenge(verification_code: str) -> str:
+        """Generates a base64 encoded sha512 encrypted version of a given string.
+        
+        :param verification_code:
+        :return: The encrypted code in base64 format.
+        """
+        
         encoded = sha512(verification_code.encode()).digest()
         return b64encode(encoded, altchars = b"_-").decode()

+ 15 - 7
cura/OAuth2/AuthorizationRequestHandler.py

@@ -14,9 +14,12 @@ if TYPE_CHECKING:
 
 catalog = i18nCatalog("cura")
 
-##  This handler handles all HTTP requests on the local web server.
-#   It also requests the access token for the 2nd stage of the OAuth flow.
 class AuthorizationRequestHandler(BaseHTTPRequestHandler):
+    """This handler handles all HTTP requests on the local web server.
+    
+    It also requests the access token for the 2nd stage of the OAuth flow.
+    """
+    
     def __init__(self, request, client_address, server) -> None:
         super().__init__(request, client_address, server)
 
@@ -55,10 +58,13 @@ class AuthorizationRequestHandler(BaseHTTPRequestHandler):
             # This will cause the server to shut down, so we do it at the very end of the request handling.
             self.authorization_callback(token_response)
 
-    ##  Handler for the callback URL redirect.
-    #   \param query Dict containing the HTTP query parameters.
-    #   \return HTTP ResponseData containing a success page to show to the user.
     def _handleCallback(self, query: Dict[Any, List]) -> Tuple[ResponseData, Optional[AuthenticationResponse]]:
+        """Handler for the callback URL redirect.
+        
+        :param query: Dict containing the HTTP query parameters.
+        :return: HTTP ResponseData containing a success page to show to the user.
+        """
+        
         code = self._queryGet(query, "code")
         state = self._queryGet(query, "state")
         if state != self.state:
@@ -95,9 +101,10 @@ class AuthorizationRequestHandler(BaseHTTPRequestHandler):
             self.authorization_helpers.settings.AUTH_FAILED_REDIRECT
         ), token_response
 
-    ##  Handle all other non-existing server calls.
     @staticmethod
     def _handleNotFound() -> ResponseData:
+        """Handle all other non-existing server calls."""
+
         return ResponseData(status = HTTP_STATUS["NOT_FOUND"], content_type = "text/html", data_stream = b"Not found.")
 
     def _sendHeaders(self, status: "ResponseStatus", content_type: str, redirect_uri: str = None) -> None:
@@ -110,7 +117,8 @@ class AuthorizationRequestHandler(BaseHTTPRequestHandler):
     def _sendData(self, data: bytes) -> None:
         self.wfile.write(data)
 
-    ##  Convenience helper for getting values from a pre-parsed query string
     @staticmethod
     def _queryGet(query_data: Dict[Any, List], key: str, default: Optional[str] = None) -> Optional[str]:
+        """Convenience helper for getting values from a pre-parsed query string"""
+
         return query_data.get(key, [default])[0]

+ 12 - 7
cura/OAuth2/AuthorizationRequestServer.py

@@ -9,21 +9,26 @@ if TYPE_CHECKING:
     from cura.OAuth2.AuthorizationHelpers import AuthorizationHelpers
 
 
-##  The authorization request callback handler server.
-#   This subclass is needed to be able to pass some data to the request handler.
-#   This cannot be done on the request handler directly as the HTTPServer
-#   creates an instance of the handler after init.
 class AuthorizationRequestServer(HTTPServer):
-    ##  Set the authorization helpers instance on the request handler.
+    """The authorization request callback handler server.
+    
+    This subclass is needed to be able to pass some data to the request handler. This cannot be done on the request
+    handler directly as the HTTPServer creates an instance of the handler after init.
+    """
+    
     def setAuthorizationHelpers(self, authorization_helpers: "AuthorizationHelpers") -> None:
+        """Set the authorization helpers instance on the request handler."""
+
         self.RequestHandlerClass.authorization_helpers = authorization_helpers  # type: ignore
 
-    ##  Set the authorization callback on the request handler.
     def setAuthorizationCallback(self, authorization_callback: Callable[["AuthenticationResponse"], Any]) -> None:
+        """Set the authorization callback on the request handler."""
+
         self.RequestHandlerClass.authorization_callback = authorization_callback  # type: ignore
 
-    ##  Set the verification code on the request handler.
     def setVerificationCode(self, verification_code: str) -> None:
+        """Set the verification code on the request handler."""
+
         self.RequestHandlerClass.verification_code = verification_code  # type: ignore
 
     def setState(self, state: str) -> None:

+ 32 - 15
cura/OAuth2/AuthorizationService.py

@@ -25,9 +25,11 @@ if TYPE_CHECKING:
     from UM.Preferences import Preferences
 
 
-##  The authorization service is responsible for handling the login flow,
-#   storing user credentials and providing account information.
 class AuthorizationService:
+    """The authorization service is responsible for handling the login flow, storing user credentials and providing
+    account information.
+    """
+    
     # Emit signal when authentication is completed.
     onAuthStateChanged = Signal()
 
@@ -59,11 +61,16 @@ class AuthorizationService:
         if self._preferences:
             self._preferences.addPreference(self._settings.AUTH_DATA_PREFERENCE_KEY, "{}")
 
-    ##  Get the user profile as obtained from the JWT (JSON Web Token).
-    #   If the JWT is not yet parsed, calling this will take care of that.
-    #   \return UserProfile if a user is logged in, None otherwise.
-    #   \sa _parseJWT
     def getUserProfile(self) -> Optional["UserProfile"]:
+        """Get the user profile as obtained from the JWT (JSON Web Token).
+        
+        If the JWT is not yet parsed, calling this will take care of that.
+        
+        :return: UserProfile if a user is logged in, None otherwise.
+        
+        See also: :py:method:`cura.OAuth2.AuthorizationService.AuthorizationService._parseJWT`
+        """
+        
         if not self._user_profile:
             # If no user profile was stored locally, we try to get it from JWT.
             try:
@@ -81,9 +88,12 @@ class AuthorizationService:
 
         return self._user_profile
 
-    ##  Tries to parse the JWT (JSON Web Token) data, which it does if all the needed data is there.
-    #   \return UserProfile if it was able to parse, None otherwise.
     def _parseJWT(self) -> Optional["UserProfile"]:
+        """Tries to parse the JWT (JSON Web Token) data, which it does if all the needed data is there.
+        
+        :return: UserProfile if it was able to parse, None otherwise.
+        """
+        
         if not self._auth_data or self._auth_data.access_token is None:
             # If no auth data exists, we should always log in again.
             Logger.log("d", "There was no auth data or access token")
@@ -106,8 +116,9 @@ class AuthorizationService:
         self._storeAuthData(self._auth_data)
         return self._auth_helpers.parseJWT(self._auth_data.access_token)
 
-    ##  Get the access token as provided by the repsonse data.
     def getAccessToken(self) -> Optional[str]:
+        """Get the access token as provided by the repsonse data."""
+
         if self._auth_data is None:
             Logger.log("d", "No auth data to retrieve the access_token from")
             return None
@@ -122,8 +133,9 @@ class AuthorizationService:
 
         return self._auth_data.access_token if self._auth_data else None
 
-    ##  Try to refresh the access token. This should be used when it has expired.
     def refreshAccessToken(self) -> None:
+        """Try to refresh the access token. This should be used when it has expired."""
+
         if self._auth_data is None or self._auth_data.refresh_token is None:
             Logger.log("w", "Unable to refresh access token, since there is no refresh token.")
             return
@@ -135,14 +147,16 @@ class AuthorizationService:
             Logger.log("w", "Failed to get a new access token from the server.")
             self.onAuthStateChanged.emit(logged_in = False)
 
-    ##  Delete the authentication data that we have stored locally (eg; logout)
     def deleteAuthData(self) -> None:
+        """Delete the authentication data that we have stored locally (eg; logout)"""
+
         if self._auth_data is not None:
             self._storeAuthData()
             self.onAuthStateChanged.emit(logged_in = False)
 
-    ##  Start the flow to become authenticated. This will start a new webbrowser tap, prompting the user to login.
     def startAuthorizationFlow(self) -> None:
+        """Start the flow to become authenticated. This will start a new webbrowser tap, prompting the user to login."""
+
         Logger.log("d", "Starting new OAuth2 flow...")
 
         # Create the tokens needed for the code challenge (PKCE) extension for OAuth2.
@@ -177,8 +191,9 @@ class AuthorizationService:
         QDesktopServices.openUrl(QUrl("{}?{}".format(self._auth_url, query_string)))
 
 
-    ##  Callback method for the authentication flow.
     def _onAuthStateChanged(self, auth_response: AuthenticationResponse) -> None:
+        """Callback method for the authentication flow."""
+
         if auth_response.success:
             self._storeAuthData(auth_response)
             self.onAuthStateChanged.emit(logged_in = True)
@@ -186,8 +201,9 @@ class AuthorizationService:
             self.onAuthenticationError.emit(logged_in = False, error_message = auth_response.err_message)
         self._server.stop()  # Stop the web server at all times.
 
-    ##  Load authentication data from preferences.
     def loadAuthDataFromPreferences(self) -> None:
+        """Load authentication data from preferences."""
+
         if self._preferences is None:
             Logger.log("e", "Unable to load authentication data, since no preference has been set!")
             return
@@ -208,8 +224,9 @@ class AuthorizationService:
         except ValueError:
             Logger.logException("w", "Could not load auth data from preferences")
 
-    ##  Store authentication data in preferences.
     def _storeAuthData(self, auth_data: Optional[AuthenticationResponse] = None) -> None:
+        """Store authentication data in preferences."""
+
         Logger.log("d", "Attempting to store the auth data")
         if self._preferences is None:
             Logger.log("e", "Unable to save authentication data, since no preference has been set!")

+ 22 - 13
cura/OAuth2/LocalAuthorizationServer.py

@@ -20,18 +20,23 @@ if TYPE_CHECKING:
 
 
 class LocalAuthorizationServer:
-    ##  The local LocalAuthorizationServer takes care of the oauth2 callbacks.
-    #   Once the flow is completed, this server should be closed down again by
-    #   calling stop()
-    #   \param auth_helpers An instance of the authorization helpers class.
-    #   \param auth_state_changed_callback A callback function to be called when
-    #   the authorization state changes.
-    #   \param daemon Whether the server thread should be run in daemon mode.
-    #   Note: Daemon threads are abruptly stopped at shutdown. Their resources
-    #   (e.g. open files) may never be released.
     def __init__(self, auth_helpers: "AuthorizationHelpers",
                  auth_state_changed_callback: Callable[["AuthenticationResponse"], Any],
                  daemon: bool) -> None:
+        """The local LocalAuthorizationServer takes care of the oauth2 callbacks.
+        
+        Once the flow is completed, this server should be closed down again by calling
+        :py:meth:`cura.OAuth2.LocalAuthorizationServer.LocalAuthorizationServer.stop()`
+        
+        :param auth_helpers: An instance of the authorization helpers class.
+        :param auth_state_changed_callback: A callback function to be called when the authorization state changes.
+        :param daemon: Whether the server thread should be run in daemon mode.
+        
+        .. note::
+        
+            Daemon threads are abruptly stopped at shutdown. Their resources (e.g. open files) may never be released.
+        """
+        
         self._web_server = None  # type: Optional[AuthorizationRequestServer]
         self._web_server_thread = None  # type: Optional[threading.Thread]
         self._web_server_port = auth_helpers.settings.CALLBACK_PORT
@@ -39,10 +44,13 @@ class LocalAuthorizationServer:
         self._auth_state_changed_callback = auth_state_changed_callback
         self._daemon = daemon
 
-    ##  Starts the local web server to handle the authorization callback.
-    #   \param verification_code The verification code part of the OAuth2 client identification.
-    #   \param state The unique state code (to ensure that the request we get back is really from the server.
     def start(self, verification_code: str, state: str) -> None:
+        """Starts the local web server to handle the authorization callback.
+        
+        :param verification_code: The verification code part of the OAuth2 client identification.
+        :param state: The unique state code (to ensure that the request we get back is really from the server.
+        """
+        
         if self._web_server:
             # If the server is already running (because of a previously aborted auth flow), we don't have to start it.
             # We still inject the new verification code though.
@@ -66,8 +74,9 @@ class LocalAuthorizationServer:
             self._web_server_thread = threading.Thread(None, self._web_server.serve_forever, daemon = self._daemon)
             self._web_server_thread.start()
 
-    ##  Stops the web server if it was running. It also does some cleanup.
     def stop(self) -> None:
+        """Stops the web server if it was running. It also does some cleanup."""
+
         Logger.log("d", "Stopping local oauth2 web server...")
 
         if self._web_server:

+ 13 - 7
cura/OAuth2/Models.py

@@ -8,8 +8,9 @@ class BaseModel:
         self.__dict__.update(kwargs)
 
 
-##  OAuth OAuth2Settings data template.
 class OAuth2Settings(BaseModel):
+    """OAuth OAuth2Settings data template."""
+
     CALLBACK_PORT = None  # type: Optional[int]
     OAUTH_SERVER_URL = None  # type: Optional[str]
     CLIENT_ID = None  # type: Optional[str]
@@ -20,16 +21,18 @@ class OAuth2Settings(BaseModel):
     AUTH_FAILED_REDIRECT = "https://ultimaker.com"  # type: str
 
 
-##  User profile data template.
 class UserProfile(BaseModel):
+    """User profile data template."""
+
     user_id = None  # type: Optional[str]
     username = None  # type: Optional[str]
     profile_image_url = None  # type: Optional[str]
 
 
-##  Authentication data template.
 class AuthenticationResponse(BaseModel):
-    """Data comes from the token response with success flag and error message added."""
+    """Authentication data template."""
+
+    # Data comes from the token response with success flag and error message added.
     success = True  # type: bool
     token_type = None  # type: Optional[str]
     access_token = None  # type: Optional[str]
@@ -40,22 +43,25 @@ class AuthenticationResponse(BaseModel):
     received_at = None  # type: Optional[str]
 
 
-##  Response status template.
 class ResponseStatus(BaseModel):
+    """Response status template."""
+
     code = 200  # type: int
     message = ""  # type: str
 
 
-##  Response data template.
 class ResponseData(BaseModel):
+    """Response data template."""
+
     status = None  # type: ResponseStatus
     data_stream = None  # type: Optional[bytes]
     redirect_uri = None  # type: Optional[str]
     content_type = "text/html"  # type: str
 
 
-##  Possible HTTP responses.
 HTTP_STATUS = {
+"""Possible HTTP responses."""
+
     "OK": ResponseStatus(code = 200, message = "OK"),
     "NOT_FOUND": ResponseStatus(code = 404, message = "NOT FOUND"),
     "REDIRECT": ResponseStatus(code = 302, message = "REDIRECT")