mirror of
https://github.com/TurboGit/hubicfuse.git
synced 2026-08-02 02:42:00 +02:00
Merge branch 'po/no-user-password-disclosure'
* po/no-user-password-disclosure: Do not output double quote for .cloudfuse content. Fix typo. Remove trailing white-spaces Update README to remove a remaining reference about user/password. Update README For security and privacy reason, totally removed ID/Password.
This commit is contained in:
@@ -13,7 +13,7 @@ BUILDING:
|
||||
For CentOS or similar,
|
||||
yum install gcc make fuse-devel curl-devel libxml2-devel \
|
||||
openssl-devel json-c-devel
|
||||
|
||||
|
||||
For Debian or Ubuntu,
|
||||
apt-get install gcc make libfuse-dev libcurl-dev \
|
||||
libcurl4-openssl-dev libxml2-dev libssl-dev libjson-c-dev
|
||||
@@ -29,27 +29,23 @@ USE:
|
||||
Your Hubic Cloud configuration can be placed in a file named
|
||||
$HOME/.cloudfuse. All the following variables are required:
|
||||
|
||||
username=[Hubic user e-mail]
|
||||
password=[Hubic password]
|
||||
client_id=[Hubic client id for the registered application]
|
||||
client_secret=[Hubic client secret for the registered application]
|
||||
redirect_uri=[Hubic redirect uri as registered for the application]
|
||||
refresh_token=[A refresh token you got from the script]
|
||||
|
||||
client_id, client_secret and redirect_uri can be retrieved from the hubic
|
||||
client_id, client_secret can be retrieved from the hubic
|
||||
web interface: https://hubic.com/home/browser/developers/
|
||||
|
||||
The refresh_token can be obtained running the script provided (hubic_token)
|
||||
or with any other method you like if you follow the example at https://api.hubic.com
|
||||
|
||||
Then you can call hubicfuse:
|
||||
|
||||
sudo hubicfuse /mnt/hubic -o noauto_cache,sync_read,allow_other
|
||||
|
||||
It is possible to pass (or override) some variables on the command line:
|
||||
|
||||
sudo hubicfuse /mnt/hubic \
|
||||
-o username='xxx',password='xxx',noauto_cache,sync_read,allow_other
|
||||
|
||||
And finaly, it can be set in /etc/fstab:
|
||||
|
||||
hubicfuse /mnt/hubic fuse username=xxx,password=xxx...,user,noauto 0 0
|
||||
hubicfuse /mnt/hubic fuse user,noauto 0 0
|
||||
|
||||
It also inherits a number of command-line arguments and mount options from
|
||||
the Fuse framework. The "-h" argument should provide a summary.
|
||||
@@ -61,7 +57,7 @@ USE AS NON-ROOT:
|
||||
$ sudo usermod -a -G fuse [username]
|
||||
|
||||
Mount using the above command without the sudo. The .cloudfuse file is
|
||||
search into the user's home.
|
||||
searched into the user's home.
|
||||
|
||||
To unmount use:
|
||||
|
||||
|
||||
+15
-73
@@ -504,13 +504,16 @@ void cloudfs_verify_ssl(int vrfy)
|
||||
}
|
||||
|
||||
static struct {
|
||||
char username[MAX_HEADER_SIZE], password[MAX_HEADER_SIZE];
|
||||
char client_id [MAX_HEADER_SIZE];
|
||||
char client_secret[MAX_HEADER_SIZE];
|
||||
char refresh_token[MAX_HEADER_SIZE];
|
||||
} reconnect_args;
|
||||
|
||||
void cloudfs_set_credentials(char *username, char *password)
|
||||
void cloudfs_set_credentials(char *client_id, char *client_secret, char *refresh_token)
|
||||
{
|
||||
strncpy(reconnect_args.username, username, sizeof(reconnect_args.username));
|
||||
strncpy(reconnect_args.password, password, sizeof(reconnect_args.password));
|
||||
strncpy(reconnect_args.client_id , client_id , sizeof(reconnect_args.client_id ));
|
||||
strncpy(reconnect_args.client_secret, client_secret, sizeof(reconnect_args.client_secret));
|
||||
strncpy(reconnect_args.refresh_token, refresh_token, sizeof(reconnect_args.refresh_token));
|
||||
}
|
||||
|
||||
struct htmlString {
|
||||
@@ -595,16 +598,15 @@ int cloudfs_connect()
|
||||
#define HUBIC_TOKEN_URL "https://api.hubic.com/oauth/token"
|
||||
#define HUBIC_AUTH_URL "https://api.hubic.com/oauth/auth"
|
||||
#define HUBIC_CRED_URL "https://api.hubic.com/1.0/account/credentials"
|
||||
#define HUBIC_CLIENT_ID (options.client_id)
|
||||
#define HUBIC_CLIENT_SECRET (options.client_secret)
|
||||
#define HUBIC_REDIRECT_URI (options.redirect_uri)
|
||||
#define HUBIC_USERNAME (options.username)
|
||||
#define HUBIC_PASSWORD (options.password)
|
||||
#define HUBIC_CLIENT_ID (reconnect_args.client_id)
|
||||
#define HUBIC_CLIENT_SECRET (reconnect_args.client_secret)
|
||||
#define HUBIC_REFRESH_TOKEN (reconnect_args.refresh_token)
|
||||
#define HUBIC_OPTIONS_SIZE 2048
|
||||
|
||||
long response = -1;
|
||||
char url[HUBIC_OPTIONS_SIZE];
|
||||
char payload[HUBIC_OPTIONS_SIZE];
|
||||
struct json_object *json_obj;
|
||||
|
||||
pthread_mutex_lock(&pool_mut);
|
||||
|
||||
@@ -627,76 +629,16 @@ int cloudfs_connect()
|
||||
curl_easy_setopt(curl, CURLOPT_POST, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc_string);
|
||||
/* Step 1 : request a token */
|
||||
|
||||
char oauthid[HUBIC_OPTIONS_SIZE];
|
||||
char auth_code[HUBIC_OPTIONS_SIZE];
|
||||
/* Step 1 : request a token - Not needed anymore with refresh_token */
|
||||
|
||||
sprintf (url, "%s?client_id=%s&redirect_uri=%s&scope=usage.r,account.r,getAllLinks.r,credentials.r,activate.w,links.drw&response_type=code&state=none",
|
||||
HUBIC_AUTH_URL, HUBIC_CLIENT_ID, curl_escape(HUBIC_REDIRECT_URI,0));
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
char *json_str = htmlStringGet(curl);
|
||||
/* Step 2 : get request code - Not needed anymore with refresh_token */
|
||||
|
||||
debugf ("HUBIC AUTH_URL (req token) result: '%s'\n", json_str);
|
||||
|
||||
struct json_object *json_obj;
|
||||
|
||||
const char *oauth_pattern = "<input type=\"hidden\" name=\"oauth\" value=\"";
|
||||
char *start, *stop;
|
||||
if ((start = strstr(json_str, oauth_pattern)) != NULL)
|
||||
{
|
||||
start += strlen(oauth_pattern);
|
||||
stop = start;
|
||||
while ((*stop >= '0') && (*stop <= '9'))
|
||||
stop++;
|
||||
strncpy(oauthid, start, stop-start);
|
||||
oauthid[stop-start]='\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
debugf ("HUBIC fails to get oauthid");
|
||||
return 0;
|
||||
}
|
||||
|
||||
debugf ("HUBIC oauthid = '%s'\n", oauthid);
|
||||
free(json_str);
|
||||
|
||||
/* Step 2 : get request code */
|
||||
|
||||
sprintf(payload, "oauth=%s&usage=r&account=r&getAllLinks=r&credentials=r&activate=w&links=r&action=accepted&login=%s&user_pwd=%s&links=w&links=d", oauthid, curl_escape(HUBIC_USERNAME,0), curl_escape(HUBIC_PASSWORD,0));
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, HUBIC_AUTH_URL);
|
||||
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_HEADER, 1); /* headers needed to get the Location */
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(payload));
|
||||
|
||||
json_str = htmlStringGet(curl);
|
||||
debugf ("HUBIC AUTH_URL result: '%s'\n", json_str);
|
||||
|
||||
const char *location_pattern = "?code=";
|
||||
if ((start = strstr(json_str, location_pattern)) != NULL)
|
||||
{
|
||||
start += strlen(location_pattern);
|
||||
stop = start;
|
||||
while (*stop != '&')
|
||||
stop++;
|
||||
strncpy(auth_code, start, stop-start);
|
||||
auth_code[stop-start]='\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
debugf ("HUBIC fails to get Location header");
|
||||
return 0;
|
||||
}
|
||||
|
||||
free(json_str);
|
||||
debugf ("HUBIC auth_code = '%s'\n", auth_code);
|
||||
|
||||
/* Step 3 : get access token */
|
||||
|
||||
sprintf(payload, "code=%s&redirect_uri=%s&grant_type=authorization_code", auth_code, curl_escape(HUBIC_REDIRECT_URI,0));
|
||||
sprintf(payload, "refresh_token=%s&grant_type=refresh_token", HUBIC_REFRESH_TOKEN);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, HUBIC_TOKEN_URL);
|
||||
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||
@@ -709,7 +651,7 @@ int cloudfs_connect()
|
||||
curl_easy_setopt(curl, CURLOPT_PASSWORD, HUBIC_CLIENT_SECRET);
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
||||
|
||||
json_str = htmlStringGet(curl);
|
||||
char *json_str = htmlStringGet(curl);
|
||||
json_obj = json_tokener_parse(json_str);
|
||||
debugf ("HUBIC TOKEN_URL result: '%s'\n", json_str);
|
||||
free(json_str);
|
||||
|
||||
+2
-4
@@ -25,17 +25,15 @@ typedef struct dir_entry
|
||||
} dir_entry;
|
||||
|
||||
typedef struct options {
|
||||
char username[OPTION_SIZE];
|
||||
char password[OPTION_SIZE];
|
||||
char cache_timeout[OPTION_SIZE];
|
||||
char verify_ssl[OPTION_SIZE];
|
||||
char client_id[OPTION_SIZE];
|
||||
char client_secret[OPTION_SIZE];
|
||||
char redirect_uri[OPTION_SIZE];
|
||||
char refresh_token[OPTION_SIZE];
|
||||
} FuseOptions;
|
||||
|
||||
void cloudfs_init(void);
|
||||
void cloudfs_set_credentials(char *username, char *password);
|
||||
void cloudfs_set_credentials(char *client_id, char *client_secret, char *refresh_token);
|
||||
int cloudfs_connect(void);
|
||||
int cloudfs_object_read_fp(const char *path, FILE *fp);
|
||||
int cloudfs_object_write_fp(const char *path, FILE *fp);
|
||||
|
||||
+9
-12
@@ -423,24 +423,20 @@ char *get_home_dir()
|
||||
}
|
||||
|
||||
FuseOptions options = {
|
||||
.username = "",
|
||||
.password = "",
|
||||
.cache_timeout = "600",
|
||||
.verify_ssl = "true",
|
||||
.client_id = "",
|
||||
.client_secret = "",
|
||||
.redirect_uri = ""
|
||||
.refresh_token = ""
|
||||
};
|
||||
|
||||
int parse_option(void *data, const char *arg, int key, struct fuse_args *outargs)
|
||||
{
|
||||
if (sscanf(arg, " username = %[^\r\n ]", options.username) ||
|
||||
sscanf(arg, " password = %[^\r\n ]", options.password) ||
|
||||
sscanf(arg, " cache_timeout = %[^\r\n ]", options.cache_timeout) ||
|
||||
if (sscanf(arg, " cache_timeout = %[^\r\n ]", options.cache_timeout) ||
|
||||
sscanf(arg, " verify_ssl = %[^\r\n ]", options.verify_ssl) ||
|
||||
sscanf(arg, " client_id = %[^\r\n ]", options.client_id) ||
|
||||
sscanf(arg, " client_secret = %[^\r\n ]", options.client_secret) ||
|
||||
sscanf(arg, " redirect_uri = %[^\r\n ]", options.redirect_uri))
|
||||
sscanf(arg, " refresh_token = %[^\r\n ]", options.refresh_token))
|
||||
return 0;
|
||||
if (!strcmp(arg, "-f") || !strcmp(arg, "-d") || !strcmp(arg, "debug"))
|
||||
cloudfs_debug(1);
|
||||
@@ -466,13 +462,14 @@ int main(int argc, char **argv)
|
||||
|
||||
cache_timeout = atoi(options.cache_timeout);
|
||||
|
||||
if (!*options.username || !*options.password)
|
||||
if (!*options.client_id || !*options.client_secret || !*options.refresh_token)
|
||||
{
|
||||
fprintf(stderr, "Unable to determine username and password.\n\n");
|
||||
fprintf(stderr, "Unable to determine client_id, client_secret or refresh_token.\n\n");
|
||||
fprintf(stderr, "These can be set either as mount options or in "
|
||||
"a file named %s\n\n", settings_filename);
|
||||
fprintf(stderr, " username=[Account username]\n");
|
||||
fprintf(stderr, " password=[Account password]\n");
|
||||
fprintf(stderr, " client_id=[App's id]\n");
|
||||
fprintf(stderr, " client_secret=[App's secret]\n");
|
||||
fprintf(stderr, " refresh_token=[Get it running hubic_token]\n");
|
||||
fprintf(stderr, "The following settings are optional:\n\n");
|
||||
fprintf(stderr, " cache_timeout=[Seconds for directory caching, default 600]\n");
|
||||
fprintf(stderr, " verify_ssl=[False to disable SSL cert verification]\n");
|
||||
@@ -484,7 +481,7 @@ int main(int argc, char **argv)
|
||||
|
||||
cloudfs_verify_ssl(!strcasecmp(options.verify_ssl, "true"));
|
||||
|
||||
cloudfs_set_credentials(options.username, options.password);
|
||||
cloudfs_set_credentials(options.client_id, options.client_secret, options.refresh_token);
|
||||
if (!cloudfs_connect())
|
||||
{
|
||||
fprintf(stderr, "Failed to authenticate.\n");
|
||||
|
||||
Executable
+556
@@ -0,0 +1,556 @@
|
||||
# !/bin/sh
|
||||
|
||||
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
# Version 2, December 2004
|
||||
|
||||
# Copyright (C) 2014 - Alain BENEDETTI
|
||||
|
||||
# Everyone is permitted to copy and distribute verbatim or modified
|
||||
# copies of this license document, and changing it is allowed as long
|
||||
# as the name is changed.
|
||||
|
||||
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
# This program is free software. It comes without any warranty, to
|
||||
# the extent permitted by applicable law. You can redistribute it
|
||||
# and/or modify it under the terms of the Do What The Fuck You Want
|
||||
# To Public License, Version 2, as published by Sam Hocevar. See
|
||||
# http://www.wtfpl.net/ for more details.
|
||||
#===========================================================
|
||||
|
||||
# Fonction :
|
||||
# ----------
|
||||
# - This utility will get you the 'refresh_token' that is needed for your
|
||||
# programm/app/script, etc... to access your hubiC objects.
|
||||
#
|
||||
# Usage :
|
||||
# -------
|
||||
# hubic_token [-k|--insecure] [-V|--version]
|
||||
# -k|--insecure: connects to the server even when certificate
|
||||
# verification fails. If certificate verification fails
|
||||
# and this option is not passed, the script will fail to
|
||||
# protect from possible security issues.
|
||||
# -V|--version : displays the version of this script and exits.
|
||||
#
|
||||
#
|
||||
# Tested : Ubuntu Trusty,
|
||||
# ------ Synology (with coreutils and curl installed as Syno wget does NOT
|
||||
# have HTTPS support compiled in)
|
||||
#
|
||||
# Depends on : - a shell (bash NOT required, dash or ash is enough)
|
||||
# ---------- - sed
|
||||
# - curl (better) or wget (with HTTPS support) as a fallback.
|
||||
# No other gnu utility is necessary to run this script!
|
||||
# Optionnaly, if you have dd and base64, you will get a better
|
||||
# random seed, but that is not mandatory at all.
|
||||
#
|
||||
# Version : 1.0.0
|
||||
# -------
|
||||
#
|
||||
# Date : 2014-11-28
|
||||
# -----
|
||||
#
|
||||
# Author : Alain BENEDETTI
|
||||
# ------
|
||||
#
|
||||
# History :
|
||||
# -------
|
||||
# 1.0.0
|
||||
# - Initial version
|
||||
#
|
||||
# Contributor : // Add your name if you contribute and redistribute //
|
||||
# -----------
|
||||
# - Sofiane, who gave me the initial raw version of the full connection script.
|
||||
#
|
||||
|
||||
# ======================================================================================
|
||||
# General notes: the script is written to work on the Ubuntu's default shell (dash),
|
||||
# -------------- thus it contains no 'bashism' and can be run easily on plaforms like
|
||||
# NAS. It is also tested with Synology (with coreutils and curl installed).
|
||||
#
|
||||
# Why this script: the oAuth used by hubiC (OVH) is now quite well documented but only
|
||||
# ---------------- partially implemented by hubiC.
|
||||
# - What is implemented: declare an "app". Here you will get the client_id and
|
||||
# client_secret necessary to authorize your app to interact. You also declare a
|
||||
# redirect_uri. This is where you should normally have to implement what is
|
||||
# missing from hubiC to get the access_token for you app! To avoid having
|
||||
# to implement anything, you can use this script instead, then the redirect_uri
|
||||
# you give can be any fake uri (as long as it is a valid uri), like for
|
||||
# example http://localhost
|
||||
# - What is NOT implemented by Hubic:
|
||||
# ... well, pretty much everything else beyond declaring your app!
|
||||
# So the rest of the process is:
|
||||
# -1) deciding which permissions you grant to your 'app'
|
||||
# -2) get the refresh_token/access_token
|
||||
# For 1), you obviously need your main hubiC user/password, but once you
|
||||
# get that refresh_token, your app can connect to your hubiC account with
|
||||
# only that, plus the app id and secret... which is the purpose of oAuth
|
||||
# (authorize an app to access some of your objects without exposing your main
|
||||
# user/password).
|
||||
# So, this is a second layer above the id/secret of the app, because id/secret
|
||||
# alone is not enough to connect.
|
||||
# Let's suppose you gave id/secret AND refresh_token to someone. He can run the
|
||||
# app, and have access to what is authorized to the app. If you want to revoke
|
||||
# the authorizations of that person, you don't need to revoke the app, just run
|
||||
# this script again. You will then get a new refresh_token for yourself to connect
|
||||
# but the old refresh_token you gave to the other person won't be valid anymore.
|
||||
#
|
||||
# Legal concern: I don't work at OVH... but from their perspective this script does
|
||||
# -------------- not more that what you could do yourself with a browser. As you
|
||||
# don't need to run it often: just once to get the refresh_token, and once more
|
||||
# if you lose that token, there should be no harm done to OVH infrastructures!
|
||||
# It will even save some calls... for example, hubicfuse does all the process
|
||||
# everytime you connect it! So with that script, we should be able to save some
|
||||
# requests!
|
||||
# ... so, unless OVH raises any objection, this should be safe... otherwise copy
|
||||
# the requests, and do them with your browser!
|
||||
#
|
||||
# Security: As this is mainly for security (and also to get better performance
|
||||
# --------- at the initialization of your app), this script does not read secrets
|
||||
# from a configuration file, nor as parameters... this is also to keep the script
|
||||
# very simple!
|
||||
# The script simply prompts for those elements.
|
||||
# Nevertheless, if you ever want to use this script in an automated way, you can:
|
||||
# - prepare a file with all the answers and redirect the input to that file.
|
||||
# - set the appropriate environment variable prior to using that script.
|
||||
# The variables are named according to their names in the hubiC requests:
|
||||
# -- client_id (The id of your app)
|
||||
# -- client_secret (The secret of your app)
|
||||
# -- redirect_uri (The redirect_uri you declared at the app creation)
|
||||
# -- scope (The authorizations you grant to the app, see hubiC's documentation).
|
||||
# -- user_login (Your main hubiC identification -the email you gave when you applied)
|
||||
# NOTE: it is called 'login' in hubiC's requests, but to avoid confusion with the
|
||||
# standard utility login, we call it here: user_login.
|
||||
# -- user_pwd (Your main hubic password)
|
||||
#
|
||||
# Embedding: This scripts writes only the version (when option -V is passed) and the
|
||||
# --------- final result if all went OK to standard output. All other messages go
|
||||
# to error output.
|
||||
# The exit code let you know what happened.
|
||||
# You can then easily embed this script, pipe it, or redirect the output to a file
|
||||
# for the rest of your script that needs the tokens.
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# Internal constants |
|
||||
# --------------------------------------------------------------------------------------
|
||||
readonly URL_AUTH='https://api.hubic.com/oauth'
|
||||
readonly VERSION='1.0.0'
|
||||
RANDOM_STR='TVq6zsU0A_GHIS6iYtbc7uc2c5jdpwIMczyMCsABJXbd'
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# Messages |
|
||||
# --------------------------------------------------------------------------------------
|
||||
# ENGLISH
|
||||
PROMPT_CLIENT_ID="client_id (the app's id): "
|
||||
PROMPT_CLIENT_SECRET="client_secret (the app's secret): "
|
||||
PROMPT_REDIRECT_URI="redirect_uri (declared at app's creation): "
|
||||
PROMPT_USER_LOGIN='user_login (the e-mail you used to subscribe): '
|
||||
PROMPT_USER_PWD="user_pwd (your hubiC's main password): "
|
||||
MSG_SCOPE="For the scope -what you authorize your app to do-, enter characters as suggested
|
||||
in parenthesis, or just hit return if you don't need the item authorized."
|
||||
PROMPT_USAGE='Get account usage (r): '
|
||||
PROMPT_GETALLLINKS='Get all published links in one call (r): '
|
||||
PROMPT_CREDENTIALS='Get OpenStack credentials, eg. access to your files (r): '
|
||||
PROMPT_ACTIVATE='Send activation email (w): '
|
||||
PROMPT_LINKS='Add new/Get/Delete published link (wrd): '
|
||||
|
||||
ERR_BAD_ARG='Unknown argument: '
|
||||
MSG_USAGE='Usage: '
|
||||
MSG_OPTIONS=" [-k|--insecure] [-V|--version]
|
||||
-k
|
||||
--insecure: connects to the server even when certificate authentication fails.
|
||||
-V
|
||||
--version : displays the version of this script and exits."
|
||||
|
||||
MSG_VERSION=", version: "
|
||||
|
||||
ERR_CURL="Can't find: curl or wget. Please intall one, ex.: sudo apt-get install curl."
|
||||
ERR_HTTPS="ERROR: certificate verification failed.
|
||||
If you want to ignore certificate verification, use -k option."
|
||||
ERR_CNX='Unexpected error trying to connect to hubiC, see error code.'
|
||||
ERR_UNEXPECTED='Unexpected response from hubiC. Do your wget/curl have HTTPS support?'
|
||||
ERR_SED="Can't find: sed. Please intall it."
|
||||
|
||||
ERR_CURL_FAILED='failed with error (see the exit code) at step'
|
||||
ERR_OAUTH_NOT_FOUND="Could not find 'oauth' in the server's response."
|
||||
ERR_OAUTH_HTTP="HTTP unexpected response code during oauth's request."
|
||||
ERR_REASON='The server said, error: '
|
||||
ERR_REASON_DESC='Error description: '
|
||||
ERR_REASON_UNKNOWN="Could not parse the error message from the server's response."
|
||||
ERR_CODE_NOT_FOUND="Could not find 'code' in the server's response."
|
||||
ERR_CODE_HTTP="HTTP unexpected response code during code's request."
|
||||
ERR_TOKEN_NOT_FOUND="Could not find 'refresh_token' in the server's response."
|
||||
ERR_TOKEN_HTTP="HTTP unexpected response code during refresh_token's request."
|
||||
|
||||
ERR_OUT='Server full response:'
|
||||
|
||||
MSG_SUCCESS='Success!'
|
||||
MSG_HEAD_RESULT='# Here is what your app needs to connect to hubiC:'
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# Intenationalization: If the message files corresponding to your language, |
|
||||
# -------------------- as detected through the LANG environment variable, are |
|
||||
# provided, they will be used instead of fallback english |
|
||||
# messages defined above. |
|
||||
# The language file must be in the same directory as the script. As LANG is of the |
|
||||
# form: LANG="fr_FR.UTF-8", we will search first for: script_name_fr_FR.txt, and |
|
||||
# if it does not exist, for script_name_fr.txt |
|
||||
# The first file that exists will be sourced here. If none exist, or if LANG is not |
|
||||
# defined in the environment variables, you get the default english messages above. |
|
||||
# --------------------------------------------------------------------------------------
|
||||
if [ -n "${LANG}" ]; then
|
||||
LANG_FILE="${0}_$( printf '%.5s' "${LANG}" ).txt"
|
||||
if [ -f "${LANG_FILE}" ]; then
|
||||
. "${LANG_FILE}"
|
||||
else
|
||||
LANG_FILE="${0}_$( printf '%.2s' "${LANG}" ).txt"
|
||||
[ -f "${LANG_FILE}" ] && . "${LANG_FILE}"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# error utility |
|
||||
# $1 is the error message we want to print out (if not empty). |
|
||||
# $2 if present, will trigger the display of the response from the server. |
|
||||
# Unless wget/curl fails, the exit code will indicate at which step we failed: |
|
||||
# -100: illegal argument |
|
||||
# -101: initialization failed |
|
||||
# -102: first request |
|
||||
# -103: second request |
|
||||
# -104: last request |
|
||||
# When wget/curl fails, this function is not used, and you get instead the exit code |
|
||||
# of wget/curl. |
|
||||
# Our codes begin are from 101 to 104 to be able to distinguish from wget/curl error |
|
||||
# codes that are from 1 to 89. |
|
||||
# --------------------------------------------------------------------------------------
|
||||
STEP=0
|
||||
|
||||
error()
|
||||
{
|
||||
[ -n "${1}" ] && echo "${1}" >&2
|
||||
if [ -n "${2}" ] && [ -n "${out}" ]; then
|
||||
echo "${ERR_OUT}" >&2
|
||||
printf -- '%s' "${out}" >&2
|
||||
fi
|
||||
exit $(( ${STEP} + 100 ))
|
||||
}
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# STEP 0: Read arguments. |
|
||||
# NOTE: to make it simple, we don't accept things like -kV because anyway it |
|
||||
# is identical to -V |
|
||||
# --------------------------------------------------------------------------------------
|
||||
V=''
|
||||
CURL_OPTS='-s'
|
||||
for arg in "$@"; do
|
||||
case "${arg}" in
|
||||
'-k' | '--insecure' )
|
||||
CURL_OPTS='-k'
|
||||
;;
|
||||
'-V' | '--version' )
|
||||
V='-V'
|
||||
;;
|
||||
*)
|
||||
echo "${ERR_BAD_ARG} '${arg}'" >&2
|
||||
error "${MSG_USAGE}$(printf -- '%s' "${0}" | sed 's|.*/||')${MSG_OPTIONS}"
|
||||
esac
|
||||
done
|
||||
if [ -n "${V}" ]; then
|
||||
echo "$(printf -- '%s' "${0}" | sed 's|.*/||')${MSG_VERSION}${VERSION}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# STEP 1: Check the existence of programs we absolutely need (no possible fallback). |
|
||||
# Note: we also test if there is https support on the detected wget/curl, plus |
|
||||
# that a connection to our hubiC URL returns a 301 (it is expected). |
|
||||
# --------------------------------------------------------------------------------------
|
||||
STEP=1
|
||||
|
||||
if [ -z "$( sed --version 2>/dev/null )" ]; then
|
||||
error 1 "${ERR_SED}"
|
||||
fi
|
||||
|
||||
if [ -z "$( curl --version 2>/dev/null )" ]; then
|
||||
if [ -z "$( wget --version 2>/dev/null )" ]; then
|
||||
error 1 "${ERR_CURL}"
|
||||
else
|
||||
CURL=wget
|
||||
CURL_DATA='--post-data'
|
||||
if [ "${CURL_OPTS}" == '-s' ]; then
|
||||
CURL_OPTS='-q'
|
||||
else
|
||||
CURL_OPTS='--no-check-certificate'
|
||||
fi
|
||||
out="$(wget -S -q "${CURL_OPTS}" --max-redirect 0 "${URL_AUTH}" -O /dev/null 2>&1)"
|
||||
ERR=$?
|
||||
[ $ERR -eq 5 ] && error "${ERR_HTTPS}"
|
||||
[ $ERR -eq 8 ] && ERR=0
|
||||
fi
|
||||
else
|
||||
CURL=curl
|
||||
CURL_DATA='--data'
|
||||
out="$(curl -i -s "${CURL_OPTS}" "${URL_AUTH}")"
|
||||
ERR=$?
|
||||
[ $ERR -eq 60 ] && error "${ERR_HTTPS}"
|
||||
fi
|
||||
if [ $ERR -ne 0 ]; then
|
||||
echo "$ERR_CNX" >&2
|
||||
exit $ERR
|
||||
else
|
||||
if [ -z "$( printf '%s' "${out}" | sed -n '/1/h;/HTTP\/1\.1 301/p;q' )" ]; then
|
||||
error "${ERR_UNEXPECTED}" 'y'
|
||||
fi
|
||||
fi
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# curl/wget wrapper. |
|
||||
# For wget, we 'trap' the exit code 8 that only means we didn't get a 200. It is a |
|
||||
# 'normal' condition as we expect some 302, and have some documented errors with 400. |
|
||||
# --------------------------------------------------------------------------------------
|
||||
ccurl()
|
||||
{
|
||||
if [ "${CURL}" = 'wget' ]; then
|
||||
out="$(wget "${CURL_OPTS}" -q -O - --max-redirect 0 -S "${@}" 2>&1 )"
|
||||
ERR=$?
|
||||
[ $ERR -eq 8 ] && return 0
|
||||
else
|
||||
out="$(curl "${CURL_OPTS}" -i -s "${@}")"
|
||||
ERR=$?
|
||||
fi
|
||||
|
||||
if [ $ERR -ne 0 ]; then
|
||||
echo "${CURL} ${ERR_CURL_FAILED} ${STEP}." >&2
|
||||
exit $ERR
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# Prompt for the variables: client_id, client_secret, etc... |
|
||||
# NOTE: we don't prompt for account basic information access in the scope, because |
|
||||
# apparently, even if you don't give it in the scope, it is always authorized. |
|
||||
# So the minimal 'scope' variable will be: scope='account.r' |
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
if [ -z "${client_id}" ]; then
|
||||
read -p "${PROMPT_CLIENT_ID}" client_id || exit $?
|
||||
fi
|
||||
|
||||
if [ -z "${client_secret}" ]; then
|
||||
read -p "${PROMPT_CLIENT_SECRET}" client_secret || exit $?
|
||||
fi
|
||||
|
||||
if [ -z "${redirect_uri}" ]; then
|
||||
read -p "${PROMPT_REDIRECT_URI}" redirect_uri || exit $?
|
||||
fi
|
||||
|
||||
if [ -z "${scope}" ]; then
|
||||
printf '\n%s\n' "${MSG_SCOPE}" >&2
|
||||
|
||||
scope='account.r'
|
||||
|
||||
read -p "${PROMPT_USAGE}" usage || exit $?
|
||||
[ "$usage" = 'r' ] && scope="${scope},usage.r"
|
||||
|
||||
read -p "${PROMPT_GETALLLINKS}" getAllLinks || exit $?
|
||||
[ "$getAllLinks" = 'r' ] && scope="${scope},getAllLinks.r"
|
||||
|
||||
read -p "${PROMPT_CREDENTIALS}" credentials || exit $?
|
||||
[ "$credentials" = 'r' ] && scope="${scope},credentials.r"
|
||||
|
||||
read -p "${PROMPT_ACTIVATE}" activate || exit $?
|
||||
[ "$activate" = 'w' ] && scope="${scope},activate.w"
|
||||
|
||||
read -p "${PROMPT_LINKS}" links || exit $?
|
||||
l="$( printf -- '%s' "${links}" | sed 's/[^\(w\|r\|d\)]//g' )"
|
||||
[ -n "$l" ] && [ "${l}" = "${links}" ] && scope="${scope},links.${l}"
|
||||
printf '\n' >&2
|
||||
fi
|
||||
|
||||
if [ -z "${user_login}" ]; then
|
||||
read -p "${PROMPT_USER_LOGIN}" user_login || exit $?
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# Each step is based on the same principle: |
|
||||
# - Prepare and send the request. |
|
||||
# - extract a string from the response. |
|
||||
# - error handling: |
|
||||
# = An error can happen during the request, in which case we exit with the |
|
||||
# return code of wget/curl. |
|
||||
# = An error can happen when trying to extract the string, if we can't find the |
|
||||
# string we search. The error message and entire server response will be |
|
||||
# displayed in this case. |
|
||||
# = There are some "documented" errors, generally indicated by a different HTTP |
|
||||
# status code. Should such error happen, we will then try to extract and |
|
||||
# display the documented message. Again if this documented message cannot be |
|
||||
# extracted, or we have another HTTP status, the whole response is dumped. |
|
||||
# |
|
||||
# STEP2: getting oauth |
|
||||
# The expected response is a html page with HTTP status 200. |
|
||||
# From this page we extract 'oauth' which is the value here: |
|
||||
# ... name="oauth" value="168341"><input type="hidden" name="action" ... |
|
||||
# Error extraction is on HTTP status 302 in the location response header. |
|
||||
# --------------------------------------------------------------------------------------
|
||||
STEP=2
|
||||
|
||||
# FALLBACK: if either dd or base64 are not found or fail, we keep the initial
|
||||
# RANDOM_STR, thus it is not random anymore, but at least the script works!
|
||||
rnd="$(dd if=/dev/urandom bs=1 count=33 2>/dev/null | base64 -w 0 2>/dev/null)" && RANDOM_STR="${rnd}"
|
||||
|
||||
URL="${URL_AUTH}/auth/?client_id=${client_id}&redirect_uri=${redirect_uri}&scope=${scope}&response_type=code&state=${RANDOM_STR}"
|
||||
|
||||
ccurl "${URL}"
|
||||
|
||||
if [ -n "$( printf '%s' "${out}" | sed -n '/1/h;/HTTP\/1\.1 200/p;q' )" ]; then
|
||||
oauth="$(echo "${out}" | sed -n '/oauth/s/.*name=\"oauth\" value=\"\(.*\)\"><input type=\"hidden\" name=\"action\".*/\1/p')"
|
||||
if [ -z "$oauth" ]; then
|
||||
error "${ERR_OAUTH_NOT_FOUND}" 'y'
|
||||
fi
|
||||
else
|
||||
echo "${ERR_OAUTH_HTTP}" >&2
|
||||
if [ -n "$( printf '%s' "${out}" | sed -n '/1/h;/HTTP\/1\.1 302/p;q' )" ]; then
|
||||
ERR="$( printf '%s' "${out}" | sed -n 's/\&.*//;/error=/s/.*error=//p' )"
|
||||
if [ -n "${ERR}" ]; then
|
||||
printf "${ERR_REASON}%s\n" "${ERR}" >&2
|
||||
ERR="$( printf '%s' "${out}" | sed -n 's/.*error_description=//;s/\&.*//p' )"
|
||||
if [ -n "${ERR}" ]; then
|
||||
printf "${ERR_REASON_DESC}%s\n" "${ERR}" >&2
|
||||
fi
|
||||
error ''
|
||||
else
|
||||
error "${ERR_REASON_UNKNOWN}" 'y'
|
||||
fi
|
||||
fi
|
||||
error '' 'y'
|
||||
fi
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# STEP3: setting app permissions and getting 'code' |
|
||||
# The expected response is a redirect (302). |
|
||||
# We extract the 'code' from the Location header of the redirect: |
|
||||
# Location: http://localhost/?code=14163171312491G7k3O0O2VGbRyk8t83&scope=usage.r ... |
|
||||
# |
|
||||
# Error extraction, if instead we get a 200, it is probably a bad login/user_pwd, so |
|
||||
# we extract the error given by the server (in the HTML page). |
|
||||
# |
|
||||
# NOTE: the user_pwd is read here for security reason, because we don't need it before |
|
||||
# this step. It is done inside a subshell to reduce its presence in memory. |
|
||||
# Also note that dash does not have the -s option for read, hence we use the |
|
||||
# stty command (hopefully it exists and works!..). We also disable tracing in |
|
||||
# the subshell, to avoid having the password displayed if the script was runned |
|
||||
# with traces on. |
|
||||
# --------------------------------------------------------------------------------------
|
||||
STEP=3
|
||||
|
||||
out="$(
|
||||
set +x
|
||||
if [ -z "${user_pwd}" ]; then
|
||||
printf -- '%s' "${PROMPT_USER_PWD}" >&2
|
||||
stty -echo 2>/dev/null
|
||||
read user_pwd || exit $?
|
||||
stty echo 2>/dev/null
|
||||
fi
|
||||
printf '\n' >&2
|
||||
POST="$( printf '%s' "${scope}" | sed 's|,|\&|g;s|\.|=|g' )&oauth=${oauth}&action=accepted&login=${user_login}&user_pwd=${user_pwd}"
|
||||
|
||||
ccurl "${URL_AUTH}/auth/" "${CURL_DATA}" "${POST}"
|
||||
|
||||
printf -- '%s' "${out}"
|
||||
)" || exit $?
|
||||
|
||||
if [ -n "$( printf '%s' "${out}" | sed -n '/1/h;/HTTP\/1\.1 302/p;q' )" ]; then
|
||||
code="$(echo "${out}" | sed -n "s/.*?code=\(.*\)\&scope.*/\1/p")"
|
||||
if [ -z "$code" ]; then
|
||||
error "${ERR_CODE_NOT_FOUND}" 'y'
|
||||
fi
|
||||
else
|
||||
echo "${ERR_CODE_HTTP}" >&2
|
||||
if [ -n "$( printf '%s' "${out}" | sed -n '/1/h;/HTTP\/1\.1 200/p;q' )" ]; then
|
||||
ERR="$( printf '%s' "${out}" | sed -n '/class="text-error"/!d;N;s/.*\n//;s/^[ \t]*//;p' )"
|
||||
if [ -n "${ERR}" ]; then
|
||||
printf "${ERR_REASON}%s\n" "${ERR}" >&2
|
||||
error ''
|
||||
else
|
||||
error "${ERR_REASON_UNKNOWN}" 'y'
|
||||
fi
|
||||
fi
|
||||
error '' 'y'
|
||||
fi
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# STEP4: getting the refresh_token |
|
||||
# The expected response is a JSON object (HTTP/1.1 200). |
|
||||
# Documented errors are with 400 and 401 return codes. We don't try to extract |
|
||||
# error strings with wget, because wget simply exits with an error when not |
|
||||
# receiving a 200 (the exception is 302 with can still be caugh). So when using |
|
||||
# wget, either this works (200) or we display the whole response. |
|
||||
# --------------------------------------------------------------------------------------
|
||||
STEP=4
|
||||
|
||||
POST="client_id=${client_id}&client_secret=${client_secret}&code=${code}&grant_type=authorization_code"
|
||||
|
||||
if [ "${CURL}" = 'wget' ]; then
|
||||
POST="${POST}&redirect_uri=${redirect_uri}"
|
||||
CURL_ENCODE='-q'
|
||||
REDIR='-q'
|
||||
else
|
||||
CURL_ENCODE='--data-urlencode'
|
||||
REDIR="redirect_uri=${redirect_uri}"
|
||||
fi
|
||||
ccurl "${URL_AUTH}/token/" \
|
||||
"${CURL_DATA}" "${POST}" \
|
||||
"${CURL_ENCODE}" "${REDIR}"
|
||||
|
||||
if [ -n "$( printf '%s' "${out}" | sed -n '/1/h;/HTTP\/1\.1 200/p;q' )" ]; then
|
||||
refresh_token="$( printf '%s' "${out}" | sed -n 's/{\"refresh_token\":\"//;s/\",\"expires_in\".*//p' )"
|
||||
if [ -z "$refresh_token" ]; then
|
||||
error "${ERR_TOKEN_NOT_FOUND}" 'y'
|
||||
fi
|
||||
else
|
||||
echo "${ERR_TOKEN_HTTP}" >&2
|
||||
if [ "${CURL}" = 'curl' ] && [ -n "$( printf '%s' "${out}" | sed -n '/1/h;/HTTP\/1\.1 40\(0\|1\)/p;q' )" ]; then
|
||||
ERR="$( printf '%s' "${out}" | sed -n 's/"}//;/"error"/s/.*"error":"//p' )"
|
||||
if [ -n "${ERR}" ]; then
|
||||
printf "${ERR_REASON}%s\n" "${ERR}" >&2
|
||||
ERR="$( printf '%s' "${out}" | sed -n 's/","error".*//;/error_description/s/{"error_description":"//p' )"
|
||||
if [ -n "${ERR}" ]; then
|
||||
printf "${ERR_REASON_DESC}%s\n" "${ERR}" >&2
|
||||
fi
|
||||
error ''
|
||||
else
|
||||
error "${ERR_REASON_UNKNOWN}" 'y'
|
||||
fi
|
||||
fi
|
||||
error '' 'y'
|
||||
fi
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# THE END: we display the final result if all was successful |
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
echo >&2
|
||||
echo "${MSG_SUCCESS}" >&2
|
||||
echo >&2
|
||||
echo >&2
|
||||
echo "${MSG_HEAD_RESULT}"
|
||||
echo "client_id=${client_id}"
|
||||
echo "client_secret=${client_secret}"
|
||||
echo "refresh_token=${refresh_token}"
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
# --------------------------------------------------------------------------------------
|
||||
# Messages |
|
||||
# --------------------------------------------------------------------------------------
|
||||
# FRENCH
|
||||
PROMPT_CLIENT_ID="client_id (l'id de l'app): "
|
||||
PROMPT_CLIENT_SECRET="client_secret (le secret de l'app): "
|
||||
PROMPT_REDIRECT_URI="redirect_uri (declaré à la création de l'app): "
|
||||
PROMPT_USER_LOGIN="user_login (l'e-mail utilisé lors de l'inscription): "
|
||||
PROMPT_USER_PWD="user_pwd (votre mot de passe hubiC): "
|
||||
MSG_SCOPE="Pour le 'scope' -ce que vous autorisez à votre app-, entrez des caractères
|
||||
suggérés entre parenthèses, ou entrée si vous ne voulez pas autoriser l'item."
|
||||
PROMPT_USAGE="Lire le quota d'usage (r): "
|
||||
PROMPT_GETALLLINKS="Lire tous les liens publiés en une fois (r): "
|
||||
PROMPT_CREDENTIALS="Obtenir les autorisations OpenStack, ie. accéder à vos fichiers (r): "
|
||||
PROMPT_ACTIVATE="Envoyer un e-mail d'activation (w): "
|
||||
PROMPT_LINKS="Créer/Lire/Supprimer un lien publié (wrd): "
|
||||
|
||||
ERR_BAD_ARG='Argument inconnu : '
|
||||
MSG_USAGE='Usage : '
|
||||
MSG_OPTIONS=" [-k|--insecure] [-V|--version]
|
||||
-k
|
||||
--insecure: se connecte même si l'authentification du certificat échoue.
|
||||
-V
|
||||
--version : affiche la version de ce script et se termine."
|
||||
|
||||
MSG_VERSION=", version : "
|
||||
|
||||
ERR_CURL="Impossible de trouver : curl or wget. Installez-en un, ex.: sudo apt-get install curl."
|
||||
ERR_HTTPS="ERREUR: vérification du certificat échouée.
|
||||
Si vous voulez ignorer la vérification du certificat, utilisez l'option -k."
|
||||
ERR_CNX="Erreur inattendue en essayant de se connecter à hubiC, voir code d'error."
|
||||
ERR_UNEXPECTED="Response inattendue d'hubiC. Votre wget/curl supporte-t-il HTTPS ?"
|
||||
ERR_SED="Impossible de trouver: sed. Merci de l'installer."
|
||||
|
||||
ERR_CURL_FAILED="a échoué (voir le code de sortie) à l'étape"
|
||||
ERR_OAUTH_NOT_FOUND="Impossible de trouver 'oauth' dans la response du serveur."
|
||||
ERR_OAUTH_HTTP="Status de la réponse HTTP inattendu lors de la requête 'oauth'."
|
||||
ERR_REASON="Le serveur a dit, erreur : "
|
||||
ERR_REASON_DESC="Description de l'erreur : "
|
||||
ERR_REASON_UNKNOWN="Impossible de récupérer le message d'erreur dans la response du serveur."
|
||||
ERR_CODE_NOT_FOUND="Impossible de trouver 'code' dans la response du serveur."
|
||||
ERR_CODE_HTTP="Status de la réponse HTTP inattendu lors de la requête 'code'."
|
||||
ERR_TOKEN_NOT_FOUND="Impossible de trouver 'refresh_token' dans la response du serveur."
|
||||
ERR_TOKEN_HTTP="Status de la réponse HTTP inattendu lors de la requête 'refresh_token'."
|
||||
|
||||
ERR_OUT="Réponse complète du serveur:"
|
||||
|
||||
MSG_SUCCESS='Succès!'
|
||||
MSG_HEAD_RESULT="# Voici de dont vote app a besoin pour se connecter à hubiC :"
|
||||
|
||||
Reference in New Issue
Block a user