Compare commits

...

8 Commits

Author SHA1 Message Date
aeddc38396
Merge pull request #5 from Phyks/ndef
Update 2FA mechanism
2024-08-21 21:44:56 +02:00
127774a1de Merge remote-tracking branch 'ndef/patch-4' into ndef 2024-08-21 21:42:00 +02:00
ea8729bbaa Merge remote-tracking branch 'ndef/patch-3' into ndef 2024-08-21 21:41:58 +02:00
7929032313 Merge remote-tracking branch 'ndef/patch-2' into ndef 2024-08-21 21:41:56 +02:00
ndx1905-github
d53ed1d58e
Update README.md 2024-08-20 16:08:07 +02:00
ndx1905-github
224658af0d
Update icloud_to_nextcloud.py
Suppression serveur web
Modif nom iphone
2024-08-20 15:51:59 +02:00
ndx1905-github
d9c018fc44
Update requirements.txt 2024-08-20 15:50:40 +02:00
ndx1905-github
bcc006fe02
Update config.example.ini
Suppression serveur
ajout 2fa + nom iphone
2024-08-20 15:49:49 +02:00
4 changed files with 26 additions and 102 deletions

View File

@ -44,10 +44,18 @@ Run `icloud` program a first time to ensure everything is running smooth:
### Using 2FA
If you enabled 2FA on your Apple iCloud account, the script will run a simple
webserver and point you to the URL on localhost to pass it the 2FA code you
will get from one of your iDevices. This makes the script trustable for some
time and this operation is only required once in a while (every month or so).
If you enabled 2FA on your Apple iCloud account, you can request the 2FA code
beforehand by :
If your device is online:
Go to Settings > [your name].
Tap Sign-In & Security > Two Factor Authentication.
Tap Get Verification Code
If your device is offline:
Go to Settings > [your name].
Tap Sign-In & Security.
A message says "Account Details Unavailable." Tap Get Verification Code.
Put this 2FA code in the config.ini file, you have one minute to run the command line in the next section.
## Usage

View File

@ -1,13 +1,11 @@
[apple]
email = apple_icloud_email
password = apple_icloud_password
code_2fa = 2fa_code
iPhone_name = iPhone
cookie_directory = COOKIE_DIRECTORY
[nextcloud]
server = https://cloud.example.com
user = nextcloud_user
password = nextcloud_password
[webserver]
host = localhost
port = 8080

View File

@ -4,78 +4,12 @@ import logging
import sys
import urllib.parse
import bottle
import requests
from pyicloud import PyiCloudService
from requests.auth import HTTPBasicAuth
class StoppableCherootServer(bottle.ServerAdapter):
"""
We need a stoppable HTTP server, which can be stopped from within a route.
This is not doable out of the box in bottle and is quite hacky using plain
WSGIRef. This is easier and cleaner with Cheroot (formally CherryPy)
backend.
"""
def run(self, handler): # pragma: no cover
from cheroot import wsgi
self.options['bind_addr'] = (self.host, self.port)
self.options['wsgi_app'] = handler
self.server = wsgi.Server(**self.options)
try:
self.server.start()
finally:
self.server.stop()
############################################
# Web app to fetch 2FA code from the user. #
############################################
code_2fa = None # Global for passing 2FA code from web app to main script
app = bottle.Bottle()
server = None
@app.route('/')
def get_2fa():
"""
Main HTTP route, display an HTML form to fetch 2FA code from user.
"""
return """
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>iCloud 2FA protection</title>
</head>
<body>
<form method="post" action="/2fa">
<p>
<label for="2FA">2FA password?</label>
<input type="text" id="2FA" name="2FA"/>
</p>
<input type="submit"/>
</form>
</body>
</html>"""
@app.post('/2fa')
def set_2fa():
"""
Handle form submission and store 2FA code to pass along the rest of the
code.
"""
global code_2fa
global server
code_2fa = bottle.request.forms.get('2FA')
server.server.stop()
return "OK"
###############
# Main script #
###############
@ -95,23 +29,16 @@ def get_icloud_location(config):
"""
Fetch latest iPhone location from iCloud
"""
global server
global code_2fa
email = config['apple']['email']
password = config['apple']['password']
code_2fa = config['apple']['code_2fa']
api = PyiCloudService(email, password=password, cookie_directory=config['apple']['cookie_directory'])
if api.requires_2fa:
print(
"Two-factor authentication required. "
f"Head over to http://{config['webserver']['host']}:{config['webserver']['port']} and fill-in the 2FA code."
)
server = StoppableCherootServer(
host=config['webserver']['host'],
port=int(config['webserver']['port'])
)
app.run(server=server)
result = api.validate_2fa_code(code_2fa)
print("Two-factor authentication required.")
code = code_2fa
result = api.validate_2fa_code(code)
print("Code validation result: %s" % result)
if not result:
@ -124,11 +51,7 @@ def get_icloud_location(config):
print("Session trust result %s" % result)
if not result:
print(
"Failed to request trust. "
"You will likely be prompted for the code again "
"in the coming weeks"
)
print("Failed to request trust. You will likely be prompted for the code again in the coming weeks")
elif api.requires_2sa:
import click
print("Two-step authentication required. Your trusted devices are:")
@ -136,11 +59,8 @@ def get_icloud_location(config):
devices = api.trusted_devices
for i, device in enumerate(devices):
print(
" %s: %s" % (
i, device.get(
'deviceName', "SMS to %s" % device.get('phoneNumber')
)
)
" %s: %s" % (i, device.get('deviceName',
"SMS to %s" % device.get('phoneNumber')))
)
device = click.prompt('Which device would you like to use?', default=0)
@ -155,10 +75,10 @@ def get_icloud_location(config):
sys.exit(1)
iphone = next(
device
for device in api.devices
if 'iPhone' in device.status()['name']
)
device
for device in api.devices
if config['apple']['iPhone_name'] in device.status()['name']
)
iphone_location = iphone.location()
iphone_status = iphone.status()

View File

@ -1,3 +1 @@
bottle
cheroot
pyicloud