This commit is contained in:
+66
-2
@@ -14,6 +14,7 @@ CORES_DIR="$HOME/.config/coredesktop/cores"
|
||||
TAILSCALE_CONF="$HOME/.config/coredesktop/tailscale.conf"
|
||||
BACKUP_CONF="$HOME/.config/coredesktop/backup.conf"
|
||||
GOA_ACCOUNT="$COREDESKTOP/configure.d/lib/goa-account"
|
||||
EMAIL_WATCH="$COREDESKTOP/system/email-watch"
|
||||
source "$COREDESKTOP/configure.d/lib/nextcloud.sh"
|
||||
[ -f "$CORE_CONF" ] && source "$CORE_CONF"
|
||||
MAIN_DOMAIN="$CORE_DOMAIN"
|
||||
@@ -40,6 +41,7 @@ core_names() {
|
||||
SUPERPANEL_DESKTOP="$HOME/.local/share/applications/core-$CORE_DOMAIN-superpanel.desktop"
|
||||
MATRIX="https://matrix.$CORE_DOMAIN"
|
||||
ELEMENT_CONFIG="$HOME/.config/Element/config.json"
|
||||
PLAUSIBLE="plausible.$CORE_DOMAIN"
|
||||
}
|
||||
|
||||
superpanel_done() {
|
||||
@@ -70,6 +72,26 @@ element_done() {
|
||||
"$ELEMENT_CONFIG" &> /dev/null
|
||||
}
|
||||
|
||||
# Tally for Plausible, a Flatpak, shows one Plausible Analytics. It follows the
|
||||
# main Core, if that runs Plausible.
|
||||
TALLY=com.cassidyjames.plausible
|
||||
|
||||
tally_installed() {
|
||||
flatpak info "$TALLY" &> /dev/null
|
||||
}
|
||||
|
||||
tally_setting() {
|
||||
flatpak run --command=gsettings "$TALLY" "$@" 2> /dev/null
|
||||
}
|
||||
|
||||
tally_done() {
|
||||
[ "$(tally_setting get "$TALLY" domain)" = "'$PLAUSIBLE'" ]
|
||||
}
|
||||
|
||||
plausible_on_core() {
|
||||
curl -sf -o /dev/null --max-time 15 "https://$PLAUSIBLE/api/health"
|
||||
}
|
||||
|
||||
# Element only follows the main Core
|
||||
core_done() {
|
||||
superpanel_done && nextcloud_done && email_done && files_done \
|
||||
@@ -293,6 +315,13 @@ setup_element() {
|
||||
echo "Element will use $MATRIX the next time it starts."
|
||||
}
|
||||
|
||||
# Tally opens the page it showed last, so forget that one too
|
||||
setup_tally() {
|
||||
tally_setting set "$TALLY" domain "$PLAUSIBLE" || return 1
|
||||
tally_setting set "$TALLY" current-url "" || return 1
|
||||
echo "Tally for Plausible will show https://$PLAUSIBLE the next time it starts. Sign in there."
|
||||
}
|
||||
|
||||
# Run a step unless it's done; a failed step doesn't stop the ones after it
|
||||
step() {
|
||||
local name="$1" title="$2"
|
||||
@@ -359,7 +388,12 @@ setup_core() {
|
||||
step files "Nextcloud files at $MOUNT"
|
||||
[ "$CORE_DOMAIN" = "$MAIN_DOMAIN" ] && element_installed \
|
||||
&& step element "Element with your Core's Matrix server"
|
||||
[ "$CORE_DOMAIN" = "$MAIN_DOMAIN" ] && tally_installed && plausible_on_core \
|
||||
&& step tally "Tally for Plausible with your Core's Plausible"
|
||||
unset PASSWORD APP_PASSWORD
|
||||
|
||||
# Pauses email if the mail server stops taking its password (system/email-watch)
|
||||
systemctl --user start coredesktop-email-watch.service 2> /dev/null
|
||||
return $STATUS
|
||||
}
|
||||
|
||||
@@ -399,10 +433,12 @@ change_password() {
|
||||
|
||||
echo
|
||||
if email_done; then
|
||||
local was_paused=""
|
||||
"$GOA_ACCOUNT" paused imap_smtp "$CORE_EMAIL" 2> /dev/null && was_paused=", and isn't paused any more"
|
||||
jq -n --arg password "$PASSWORD" '{"imap-password": $password, "smtp-password": $password}' \
|
||||
| "$GOA_ACCOUNT" update imap_smtp "$CORE_EMAIL" 2> /dev/null
|
||||
case $? in
|
||||
0) echo " Email: has your new password" ;;
|
||||
0) echo " Email: has your new password$was_paused" ;;
|
||||
3) echo -e " \e[31mEmail: saved your new password, but the mail server didn't accept it.\e[0m"
|
||||
STATUS=1 ;;
|
||||
*) echo -e " \e[31mEmail: couldn't save your new password.\e[0m"; STATUS=1 ;;
|
||||
@@ -462,6 +498,10 @@ main_changed() {
|
||||
if element_installed && ! element_done && confirm_yes "Point Element at $MATRIX?"; then
|
||||
setup_element
|
||||
fi
|
||||
if tally_installed && ! tally_done && plausible_on_core \
|
||||
&& confirm_yes "Point Tally for Plausible at https://$PLAUSIBLE?"; then
|
||||
setup_tally
|
||||
fi
|
||||
[ -f "$TAILSCALE_CONF" ] && echo "To move this computer to its Tailscale network: configure tailscale"
|
||||
[ -f "$BACKUP_CONF" ] && echo "To back up to it instead: configure backup"
|
||||
return 0
|
||||
@@ -547,13 +587,26 @@ failed() {
|
||||
# Test an Online Accounts account, and that none of what CoreDesktop set it up
|
||||
# for is turned off in Settings:
|
||||
# <title> <provider> <identity> <setup> <fix> <enable>
|
||||
# One Online Accounts already found failing isn't signed in to again: each
|
||||
# wrong password counts towards the mail server blocking this computer.
|
||||
test_account() {
|
||||
local error off works=1
|
||||
if ! "$GOA_ACCOUNT" exists "$2" "$3" 2> /dev/null; then
|
||||
failed "$1" "not set up" "$4"
|
||||
return
|
||||
fi
|
||||
if ! error="$("$GOA_ACCOUNT" check "$2" "$3" 2>&1)"; then
|
||||
if "$GOA_ACCOUNT" paused "$2" "$3" 2> /dev/null; then
|
||||
failed "$1" "paused, as the server stopped taking its password" "$5" \
|
||||
"So this computer doesn't keep trying it. Did your Core password change?"
|
||||
works=0
|
||||
elif "$GOA_ACCOUNT" attention "$2" "$3" 2> /dev/null; then
|
||||
failed "$1" "the server doesn't take its password" "$5" "Did your Core password change?"
|
||||
works=0
|
||||
# Check email's password with Nextcloud first, not the mail server
|
||||
elif [ "$2" = imap_smtp ] && { "$EMAIL_WATCH" --check-password "$3"; [ $? -eq 2 ]; }; then
|
||||
failed "$1" "has an old password: Nextcloud doesn't take it" "$5" "Did your Core password change?"
|
||||
works=0
|
||||
elif ! error="$("$GOA_ACCOUNT" check "$2" "$3" 2>&1)"; then
|
||||
failed "$1" "can't sign in" "$5" "$error"
|
||||
works=0
|
||||
fi
|
||||
@@ -592,6 +645,16 @@ test_element() {
|
||||
fi
|
||||
}
|
||||
|
||||
test_tally() {
|
||||
local title="Tally for Plausible with your Core's Plausible"
|
||||
plausible_on_core || return 0
|
||||
if tally_done; then
|
||||
passed "$title"
|
||||
else
|
||||
failed "$title" "Tally shows another Plausible" setup_tally
|
||||
fi
|
||||
}
|
||||
|
||||
test_backups() {
|
||||
local webdav
|
||||
webdav="$(source "$BACKUP_CONF" && echo "$BACKUP_WEBDAV")"
|
||||
@@ -631,6 +694,7 @@ test_core() {
|
||||
test_account "Email in Online Accounts" imap_smtp "$CORE_EMAIL" setup_email fix_email enable_email
|
||||
test_files
|
||||
[ "$CORE_DOMAIN" = "$MAIN_DOMAIN" ] && element_installed && test_element
|
||||
[ "$CORE_DOMAIN" = "$MAIN_DOMAIN" ] && tally_installed && test_tally
|
||||
backup_on "$CORE_DOMAIN" && test_backups
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ Usage:
|
||||
goa-account check <provider> <presentation-identity>
|
||||
goa-account disabled <provider> <presentation-identity>
|
||||
goa-account enable <provider> <presentation-identity>
|
||||
goa-account attention <provider> <presentation-identity>
|
||||
goa-account pause <provider> <presentation-identity>
|
||||
goa-account paused <provider> <presentation-identity>
|
||||
goa-account remove <provider> <presentation-identity>
|
||||
|
||||
account.json:
|
||||
@@ -26,6 +29,18 @@ check signs in with what's saved, and fails if the server says no. update
|
||||
exits with 3 if it saved the password but the server didn't accept it.
|
||||
disabled prints the features CoreDesktop uses that are turned off in Settings,
|
||||
e.g. "Calendar, Files", and enable turns them back on.
|
||||
|
||||
attention exits 0 if Online Accounts found that the server no longer takes
|
||||
the saved password (it then shows the account as needing attention). It
|
||||
doesn't sign in, so it doesn't add to the server's count of failed logins.
|
||||
Online Accounts 3.58 only finds this for Nextcloud: a mail server refusing the
|
||||
password is reported as a general failure (see system/email-watch).
|
||||
|
||||
pause empties the account's saved password, keeping its place in the keyring.
|
||||
Online Accounts signs in again whenever the network changes, and Evolution
|
||||
whenever it fetches mail; with no password neither tries, so a mail server
|
||||
with fail2ban doesn't block this computer for repeated wrong passwords.
|
||||
update puts a password back, which ends the pause. paused exits 0 if paused.
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -112,6 +127,45 @@ def store_credentials(provider, account_id, credentials):
|
||||
secret, None)
|
||||
|
||||
|
||||
# The account's saved credentials, e.g. {"imap-password": "..."}
|
||||
def lookup_credentials(provider, account_id):
|
||||
items = Secret.password_search_sync(
|
||||
None, {"xdg:schema": "org.gnome.OnlineAccounts"},
|
||||
Secret.SearchFlags.ALL | Secret.SearchFlags.UNLOCK | Secret.SearchFlags.LOAD_SECRETS, None)
|
||||
for item in items:
|
||||
identity = item.get_attributes().get("goa-identity", "")
|
||||
if identity.startswith(f"{provider}:gen") and identity.endswith(f":{account_id}"):
|
||||
return GLib.Variant.parse(None, item.get_secret().get_text(), None, None).unpack()
|
||||
raise LookupError(f"there's no saved password for {account_id} in the keyring")
|
||||
|
||||
|
||||
# The credential each kind of account signs in with
|
||||
PASSWORD_KEYS = {"owncloud": "password", "imap_smtp": "imap-password"}
|
||||
|
||||
|
||||
def attention(bus, provider, presentation_identity):
|
||||
found = find_accounts(bus, provider, presentation_identity)
|
||||
if not found:
|
||||
raise LookupError(f"there's no {provider} account for {presentation_identity}")
|
||||
return any(account.get("AttentionNeeded") for _, account in found)
|
||||
|
||||
|
||||
def pause(bus, provider, presentation_identity):
|
||||
found = find_accounts(bus, provider, presentation_identity)
|
||||
if not found:
|
||||
raise LookupError(f"there's no {provider} account for {presentation_identity}")
|
||||
for _, account in found:
|
||||
store_credentials(provider, account["Id"], {})
|
||||
|
||||
|
||||
def paused(bus, provider, presentation_identity):
|
||||
found = find_accounts(bus, provider, presentation_identity)
|
||||
if not found:
|
||||
raise LookupError(f"there's no {provider} account for {presentation_identity}")
|
||||
return any(PASSWORD_KEYS[provider] not in lookup_credentials(provider, account["Id"])
|
||||
for _, account in found)
|
||||
|
||||
|
||||
def update(bus, provider, presentation_identity, credentials):
|
||||
found = find_accounts(bus, provider, presentation_identity)
|
||||
if not found:
|
||||
@@ -180,6 +234,13 @@ def main():
|
||||
if len(sys.argv) == 4 and sys.argv[1] == "enable":
|
||||
enable(bus, sys.argv[2], sys.argv[3])
|
||||
return 0
|
||||
if len(sys.argv) == 4 and sys.argv[1] == "attention":
|
||||
return 0 if attention(bus, sys.argv[2], sys.argv[3]) else 1
|
||||
if len(sys.argv) == 4 and sys.argv[1] == "pause":
|
||||
pause(bus, sys.argv[2], sys.argv[3])
|
||||
return 0
|
||||
if len(sys.argv) == 4 and sys.argv[1] == "paused":
|
||||
return 0 if paused(bus, sys.argv[2], sys.argv[3]) else 1
|
||||
if len(sys.argv) == 4 and sys.argv[1] == "remove":
|
||||
remove(bus, sys.argv[2], sys.argv[3])
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user