Open a-t-0 opened 3 years ago
<p>Rethought workflow:</p>
Install tw
Install gcalsync
Start a job in powershell
Content:
2.1 Scans existence of the of the url.txt
file with infinite loop
2.2 Once found url.txt
,
2.2.1 gets the url,
2.2.2 puts the redirect hardcoded local host inbetween,
2.2.3 Finds the preferred/default browser
2.2.4 Opens the link in the default browser
2.3 In the meantime, the normal installation shell also shows: please visit.. url
.
so that even if the user accidentally closes the browser, the user is still able to grant access to the taskwarrior google calendar sync.
Run the command that shows the Please visit request, directly in the powershell installation script.
Wait untill the request is granted, then proceed with the rest of the installation.
TODO: first install all non-user-dependent stuff like timewarrior etc. so that, if the user does not successfully activate this sync, the rest of the installation is still successful.
<p>Todo:</p>
Output the InjectCode.sh
from java CreateLines
Output the askSync.sh
from java CreateLines
Specify the location of the url.txt
(Need the username for that which isn't in HardCoded).
Specify the location of InjectCode.sh
Specify the location of askSync.sh
Run the InjectCode.sh
once before asking the permission with tw_gcal_sync..
command.
Then run the askSync.sh
command
That's it, then it should automatically open the request for permission in the browser, while simultaneously typing the result in the powershell installer.
No need to put the url.txt
file scanner in a job.
Perhaps roll back 1 commit to prevent error with "currentPath".
<p>The <code>run_local_server()</code> internally creates a new (random) state when it is executed. This means the url that is accepted is also randomly changed, as it contains that state.<br>
Method authorization_urL()
also creates it's own new random state. That means there is a difference between the state in the url you output, and the state in the url that is listened to by the local server.
To solve this, instead of importing the default method run_local_server()
from some library, import it from a custom made file named run_local_server.py
(or even just from a class at the bottom (appended) of the code. And inject the 3 lines that output the url inside the modified run_local_server()
.
Source file:
https://google-auth-oauthlib.readthedocs.io/en/latest/_modules/google_auth_oauthlib/flow.html#InstalledAppFlow.run_local_server
contains:
a lot of lines.
<p>The modified file is called: <code>customLocalServer.py</code></p>
This file is placed in /home/<linux username>/maintenance/gCal/
since the importing looks from the directory where the command is given.
# Copyright 2016 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""OAuth 2.0 Authorization Flow
This module provides integration with `requests-oauthlib`_ for running the
`OAuth 2.0 Authorization Flow`_ and acquiring user credentials.
Here's an example of using :class:`Flow` with the installed application
authorization flow::
from google_auth_oauthlib.flow import Flow
# Create the flow using the client secrets file from the Google API
# Console.
flow = Flow.from_client_secrets_file(
'path/to/client_secrets.json',
scopes=['profile', 'email'],
redirect_uri='urn:ietf:wg:oauth:2.0:oob')
# Tell the user to go to the authorization URL.
auth_url, _ = flow.authorization_url(prompt='consent')
print('Please go to this URL: {}'.format(auth_url))
# The user will get an authorization code. This code is used to get the
# access token.
code = input('Enter the authorization code: ')
flow.fetch_token(code=code)
# You can use flow.credentials, or you can just get a requests session
# using flow.authorized_session.
session = flow.authorized_session()
print(session.get('https://www.googleapis.com/userinfo/v2/me').json())
This particular flow can be handled entirely by using
:class:`InstalledAppFlow`.
.. _requests-oauthlib: http://requests-oauthlib.readthedocs.io/en/stable/
.. _OAuth 2.0 Authorization Flow:
https://tools.ietf.org/html/rfc6749#section-1.2
"""
from base64 import urlsafe_b64encode
import hashlib
import json
import logging
try:
from secrets import SystemRandom
except ImportError: # pragma: NO COVER
from random import SystemRandom
from string import ascii_letters, digits
import webbrowser
import wsgiref.simple_server
import wsgiref.util
import google.auth.transport.requests
import google.oauth2.credentials
from six.moves import input
import google_auth_oauthlib.helpers
_LOGGER = logging.getLogger(__name__)
class InstalledAppFlow(Flow):
"""Authorization flow helper for installed applications.
This :class:`Flow` subclass makes it easier to perform the
`Installed Application Authorization Flow`_. This flow is useful for
local development or applications that are installed on a desktop operating
system.
This flow has two strategies: The console strategy provided by
:meth:`run_console` and the local server strategy provided by
:meth:`run_local_server`.
Example::
from google_auth_oauthlib.flow import InstalledAppFlow
flow = InstalledAppFlow.from_client_secrets_file(
'client_secrets.json',
scopes=['profile', 'email'])
flow.run_local_server()
session = flow.authorized_session()
profile_info = session.get(
'https://www.googleapis.com/userinfo/v2/me').json()
print(profile_info)
# {'name': '...', 'email': '...', ...}
Note that these aren't the only two ways to accomplish the installed
application flow, they are just the most common ways. You can use the
:class:`Flow` class to perform the same flow with different methods of
presenting the authorization URL to the user or obtaining the authorization
response, such as using an embedded web view.
.. _Installed Application Authorization Flow:
https://developers.google.com/api-client-library/python/auth
/installed-app
"""
_OOB_REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
_DEFAULT_AUTH_PROMPT_MESSAGE = (
'Please visit this URL to authorize this application: {url}')
"""str: The message to display when prompting the user for
authorization."""
_DEFAULT_AUTH_CODE_MESSAGE = (
'Enter the authorization code: ')
"""str: The message to display when prompting the user for the
authorization code. Used only by the console strategy."""
_DEFAULT_WEB_SUCCESS_MESSAGE = (
'The authentication flow has completed, you may close this window.')
def run_console(
self,
authorization_prompt_message=_DEFAULT_AUTH_PROMPT_MESSAGE,
authorization_code_message=_DEFAULT_AUTH_CODE_MESSAGE,
**kwargs):
"""Run the flow using the console strategy.
The console strategy instructs the user to open the authorization URL
in their browser. Once the authorization is complete the authorization
server will give the user a code. The user then must copy & paste this
code into the application. The code is then exchanged for a token.
Args:
authorization_prompt_message (str): The message to display to tell
the user to navigate to the authorization URL.
authorization_code_message (str): The message to display when
prompting the user for the authorization code.
kwargs: Additional keyword arguments passed through to
:meth:`authorization_url`.
Returns:
google.oauth2.credentials.Credentials: The OAuth 2.0 credentials
for the user.
"""
kwargs.setdefault('prompt', 'consent')
self.redirect_uri = self._OOB_REDIRECT_URI
auth_url, _ = self.authorization_url(**kwargs)
print(authorization_prompt_message.format(url=auth_url))
code = input(authorization_code_message)
self.fetch_token(code=code)
return self.credentials
def run_local_server(
self, host='localhost', port=8080,
authorization_prompt_message=_DEFAULT_AUTH_PROMPT_MESSAGE,
success_message=_DEFAULT_WEB_SUCCESS_MESSAGE,
open_browser=True,
**kwargs):
"""Run the flow using the server strategy.
The server strategy instructs the user to open the authorization URL in
their browser and will attempt to automatically open the URL for them.
It will start a local web server to listen for the authorization
response. Once authorization is complete the authorization server will
redirect the user's browser to the local web server. The web server
will get the authorization code from the response and shutdown. The
code is then exchanged for a token.
Args:
host (str): The hostname for the local redirect server. This will
be served over http, not https.
port (int): The port for the local redirect server.
authorization_prompt_message (str): The message to display to tell
the user to navigate to the authorization URL.
success_message (str): The message to display in the web browser
the authorization flow is complete.
open_browser (bool): Whether or not to open the authorization URL
in the user's browser.
kwargs: Additional keyword arguments passed through to
:meth:`authorization_url`.
Returns:
google.oauth2.credentials.Credentials: The OAuth 2.0 credentials
for the user.
"""
wsgi_app = _RedirectWSGIApp(success_message)
local_server = wsgiref.simple_server.make_server(
host, port, wsgi_app, handler_class=_WSGIRequestHandler)
self.redirect_uri = 'http://{}:{}/'.format(
host, local_server.server_port)
auth_url, _ = self.authorization_url(**kwargs)
if open_browser:
webbrowser.open(auth_url, new=1, autoraise=True)
print(authorization_prompt_message.format(url=auth_url))
local_server.handle_request()
# Note: using https here because oauthlib is very picky that
# OAuth 2.0 should only occur over https.
authorization_response = wsgi_app.last_request_uri.replace(
'http', 'https')
self.fetch_token(authorization_response=authorization_response)
return self.credentials
And inserted right below auth_url, _ = self.authorization_url(**kwargs)
:
file=open("url.txt","w")
file.write(auth_url)
file.close()
Furthermore, insted of inserting the above 3 lines into GCalSide.py
, the following line is modified:
From:
from google_auth_oauthlib.flow import InstalledAppFlow
To:
from customLocalServer import InstalledAppFlow
<p>Summary of code flow:</p>
askSync.sh
script is exported from resources to /home/<linux username>/maintenance/
by the AutoInstallTaskwarrior.jar
. (and made runnable with chmod)injectCode.sh
and customLocalHost.py
are exported to /home/<linux username>/maintenance/
by the AutoInstallTaskwarrior.jar
. (and made runnable with chmod)gCal
folder that needs to be empty before the repository is cloned into it).injectCode.sh
and customLocalHost.py
are MOVED from /home/<linux username>/maintenance/
to: /home/<linux username>/maintenance/gCal/taskw_gcal_sync
by the setup.ps1
injectCode.sh
is executed to modify the repo's /taskw_gcal_sync/GCalSide.py
to import the custom google auth Flow object, (modification = exports auth url to txt.)url.txt
is created. (If it is found it copies it and opens a website in windows with that url.setup.ps1
runs askSync.sh
which executes the first taskwarrior google sync requests, which generates/outputs the authenthication url, that is then picked up by the background job.setup.ps1
continues with the next line, which stops and deletes the job. <p>Note the state is changed between the creation of the authorization by gcal, and the link opening by the wsl.</p>
name:
firstGcalSync.sh
location:
Taskwarrior-installation\AutoInstallTaskwarrior\src\main\resources\autoinstalltaskwarrior/
how: Using java class
CreateFiles.java
of projectAutoInstallTaskwarrior
.When: during the installation/execution of AutoInstallTaskwarrior.jar
Content:
where
testlinuxname
is replaced with the variable linux user name.then after having installed taskwarrior, install
tw_gcal_sync
.Then find:
name:
GCalSide.py
location:/home//maintenance/gCal/taskw_gcal_sync/`
Then find the line:
creds = flow.run_local_server()
(around line 188) and put the following code above it:Then copy:
name:
firstGCalSync.sh
location:
Taskwarrior-installation\AutoInstallTaskwarrior\src\main\resources\autoinstalltaskwarrior/
to the place dependending on protocol to make code run once at startup.Then, start a background loop script with powershell start process that
Content:
TODO: Determine whether you can run the command inside powershell, automatically open the browser in the background, this way, you don't run the risk of the user accidentally closing the popup window. It is just the background process that is a limiting factor.
firstGcalSync.sh
.Name: tbd
location: tbd
Content:
Without the abruption, that waits, until the user has granted permission. Once the user has granted permission, delete the
url.txt
file