diff options
-rw-r--r-- | shell_utils.sh | 6 | ||||
-rwxr-xr-x | upload_amo.sh | 112 |
2 files changed, 96 insertions, 22 deletions
diff --git a/shell_utils.sh b/shell_utils.sh index 95e0d4e..30dfead 100644 --- a/shell_utils.sh +++ b/shell_utils.sh @@ -6,6 +6,12 @@ ENDL=" " +# A "raw" echo, interprets neither backclash escapes nor command-line options. +# Does not emit trailing newline. +ech() { + printf %s "$*" +} + errcho() { echo "$@" >&2 } diff --git a/upload_amo.sh b/upload_amo.sh index 3ac7028..2d16c63 100755 --- a/upload_amo.sh +++ b/upload_amo.sh @@ -24,27 +24,45 @@ set -e +. ./shell_utils.sh + +_PROG_NAME="$0" +OPERATION="$1" +API_KEY="$2" +SECRET="$3" +XPI_PATH="$4" + base64url() { - echo -n "$1" | base64 -w 0 | tr '/+' '_-' | tr -d '=' + ech "$1" | base64 -w 0 | tr '/+' '_-' | tr -d '=' } sha256hmac() { - base64url "$(echo -n "$2" | openssl dgst -sha256 -hmac "$1" -binary -)" + base64url "$(ech "$2" | openssl dgst -sha256 -hmac "$1" -binary -)" } -if [ $# != 3 ]; then - echo "Usage: $0 API_KEY SECRET XPI_PATH" 1>&2 - exit 1 -fi +escape_regex_special() { + ech "$1" | sed 's/\([]\.*?{},()[-]\)/\\\1/g' +} + +# Note: We don't actually parse JSON. We extract needed keys with sed regexes +# which does not work in the general case but is sufficient for now. +get_json_key() { + local KEY_REG="$(escape_regex_special "$1")" + ech "$2" | + sed 's/\(.*"'"$KEY_REG"'"[[:space:]]*:[[:space:]]*"\([^"]*\)"\)\?.*/\2/' | + grep . | head -1 +} + +get_manifest_key() { + get_json_key "$1" "$(unzip -p "$2" manifest.json)" +} -API_KEY="$1" -SECRET="$2" -XPI_PATH="$3" -JWT_HEAD='{"alg":"HS256", "typ":"JWT"}' -JWT_ID=$(dd if=/dev/random bs=21 count=1 2>/dev/null | base64) -ISSUED_AT_TIME=$(date -u +%s) -EXPIRATION_TIME=$((ISSUED_AT_TIME + 300)) -JWT_PAYLOAD=$(cat <<EOF +generate_jwt() { + local JWT_HEAD='{"alg":"HS256", "typ":"JWT"}' + local JWT_ID=$(dd if=/dev/random bs=21 count=1 2>/dev/null | base64) + local ISSUED_AT_TIME=$(date -u +%s) + local EXPIRATION_TIME=$((ISSUED_AT_TIME + 300)) + local JWT_PAYLOAD="$(cat <<EOF { "iss": "$API_KEY", "jti": "$JWT_ID", @@ -52,13 +70,63 @@ JWT_PAYLOAD=$(cat <<EOF "exp": $EXPIRATION_TIME } EOF - ) -JWT_MESSAGE=$(base64url "$JWT_HEAD").$(base64url "$JWT_PAYLOAD") -JWT_SIGNATURE=$(sha256hmac "$SECRET" "$JWT_MESSAGE") -JWT=$JWT_MESSAGE.$JWT_SIGNATURE + )" + local JWT_MESSAGE=$(base64url "$JWT_HEAD").$(base64url "$JWT_PAYLOAD") + local JWT_SIGNATURE=$(sha256hmac "$SECRET" "$JWT_MESSAGE") + local JWT=$JWT_MESSAGE.$JWT_SIGNATURE + errcho "Using JWT: $JWT" + ech $JWT +} + +get_extension_url() { + EXTENSION_ID="$(get_manifest_key id "$XPI_PATH")" + EXTENSION_VER="$(get_manifest_key version "$XPI_PATH")" -# Query one of Mozilla endpoints to verify that JWT authentication works. -curl "https://addons.mozilla.org/api/v5/accounts/profile/" \ - -H "Authorization: JWT $JWT" + if [ -z "$EXTENSION_ID" -o -z "$EXTENSION_VER" ]; then + errcho "Couldn't extract extension id and version. Please check if $XPI_PATH contains proper manifest.json file." + exit 1 + fi -# TODO: Do the actual upload. + ech "https://addons.mozilla.org/api/v4/addons/$EXTENSION_ID/versions/$EXTENSION_VER/" +} + +usage() { + errcho "Usage: $_PROG_NAME upload|check|test API_KEY SECRET XPI_PATH" +} + +if [ $# != 4 ]; then + usage + exit 1 +fi + +unset RETURNED_DATA + +case "$OPERATION" in + test) + curl "https://addons.mozilla.org/api/v4/accounts/profile/" \ + -g -H "Authorization: JWT $(generate_jwt)" + echo + ;; + check) + RETURNED_DATA="$(curl $(get_extension_url) \ + -g -H "Authorization: JWT $(generate_jwt)")" + ;; + upload) + RETURNED_DATA="$(curl $(get_extension_url) \ + -g -XPUT --form "upload=@$XPI_PATH" \ + -H "Authorization: JWT $(generate_jwt)")" + ;; + *) + usage + exit 1 + ;; +esac + +if [ -n "$RETURNED_DATA" ]; then + printf "addons.mozilla.org says:\n%s\n" "$RETURNED_DATA" + DOWNLOAD_URL="$(get_json_key download_url "$RETURNED_DATA")" + if [ -n "$DOWNLOAD_URL" ]; then + printf "Downloading extension file from %s\n" "$DOWNLOAD_URL" + curl "$DOWNLOAD_URL" -g -H "Authorization: JWT $(generate_jwt)" -O + fi +fi |