reborked the token system
This commit is contained in:
@@ -71,8 +71,7 @@ Operation is one of download, direct_download, zip_download, or upload
|
|||||||
- shares.json stores hashed version of password.
|
- shares.json stores hashed version of password.
|
||||||
- Additionally, it may store plain text password, if users so wish.
|
- Additionally, it may store plain text password, if users so wish.
|
||||||
- Internally, Flees only compares the hashes of passwords
|
- Internally, Flees only compares the hashes of passwords
|
||||||
- Tokens are encrypted versions of the hash. (login/upload/download with
|
- Tokens are secret strings that allow login/upload/download with
|
||||||
direct links). i.e. decrypted URL request equals password hash
|
direct links. You can have many tokens for single share.
|
||||||
- Encryption key is the app_secret_key
|
|
||||||
- Direct download token is (password hash + filename) hashed
|
- Direct download token is (password hash + filename) hashed
|
||||||
|
|
||||||
|
|||||||
87
code/app.py
87
code/app.py
@@ -40,7 +40,6 @@ if 'notifier' in config_values:
|
|||||||
|
|
||||||
app.secret_key = config_values['app_secret_key']
|
app.secret_key = config_values['app_secret_key']
|
||||||
app.wsgi_app = ReverseProxied(app.wsgi_app)
|
app.wsgi_app = ReverseProxied(app.wsgi_app)
|
||||||
app.config['CRYPTO'] = Crypto(app.secret_key)
|
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def before_request():
|
def before_request():
|
||||||
@@ -80,16 +79,14 @@ def authenticate(name):
|
|||||||
session[name] = password_hash(user_password, app.secret_key)
|
session[name] = password_hash(user_password, app.secret_key)
|
||||||
return redirect(url_for('list_view',name=name))
|
return redirect(url_for('list_view',name=name))
|
||||||
|
|
||||||
@app.route('/upload/<name>/<password>', methods=['POST'])
|
@app.route('/upload/<name>/<token>', methods=['POST'])
|
||||||
@app.route('/upload', methods=['POST'])
|
@app.route('/upload', methods=['POST'])
|
||||||
def upload(name = None, password = None):
|
def upload(name = None, token = None):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
file = request.files['file']
|
file = request.files['file']
|
||||||
if name == None:
|
if name == None:
|
||||||
name = request.form['name']
|
name = request.form['name']
|
||||||
if password != None:
|
(ok,share) = get_share(name, token = token)
|
||||||
session[name] = app.config['CRYPTO'].decrypt(password)
|
|
||||||
(ok,share) = get_share(name)
|
|
||||||
if not ok:
|
if not ok:
|
||||||
return share
|
return share
|
||||||
if not get_or_none('upload', share) == True:
|
if not get_or_none('upload', share) == True:
|
||||||
@@ -121,11 +118,10 @@ def upload(name = None, password = None):
|
|||||||
return "Use the 'file' variable to upload\n",400
|
return "Use the 'file' variable to upload\n",400
|
||||||
|
|
||||||
|
|
||||||
@app.route('/upload_join/<name>/<password>', methods=['POST'])
|
@app.route('/upload_join/<name>/<token>', methods=['POST'])
|
||||||
def upload_join_splitted(name, password):
|
def upload_join_splitted(name, token):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
session[name] = app.config['CRYPTO'].decrypt(password)
|
(ok,share) = get_share(name, token = token)
|
||||||
(ok,share) = get_share(name)
|
|
||||||
if not ok:
|
if not ok:
|
||||||
return share
|
return share
|
||||||
if not get_or_none('upload', share) == True:
|
if not get_or_none('upload', share) == True:
|
||||||
@@ -173,15 +169,16 @@ def send(name):
|
|||||||
return share
|
return share
|
||||||
return render_template('send.html',name=name)
|
return render_template('send.html',name=name)
|
||||||
|
|
||||||
@app.route('/list/<name>/<password>', methods=['GET'])
|
@app.route('/list/<name>/<token>', methods=['GET'])
|
||||||
@app.route('/list/<name>', methods=['GET'])
|
@app.route('/list/<name>', methods=['GET'])
|
||||||
def list_view(name, password = None):
|
def list_view(name, token = None):
|
||||||
if password != None:
|
(ok,share) = get_share(name, token = token)
|
||||||
session[name] = app.config['CRYPTO'].decrypt(password)
|
|
||||||
return redirect(url_for('list_view',name=name))
|
|
||||||
(ok,share) = get_share(name)
|
|
||||||
if not ok:
|
if not ok:
|
||||||
return share
|
return share
|
||||||
|
if token != None and 'pass_hash' in share:
|
||||||
|
session[name] = share['pass_hash']
|
||||||
|
return redirect(url_for('list_view',name=name))
|
||||||
|
|
||||||
files = []
|
files = []
|
||||||
for file in sorted(os.listdir(share['path'])):
|
for file in sorted(os.listdir(share['path'])):
|
||||||
fp = os.path.join(share['path'],file)
|
fp = os.path.join(share['path'],file)
|
||||||
@@ -247,12 +244,10 @@ def download_direct(name,token,filename):
|
|||||||
return send_from_directory(directory=share['path'], filename=filename)
|
return send_from_directory(directory=share['path'], filename=filename)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/download/<name>/<password>/<filename>', methods=['GET'])
|
@app.route('/download/<name>/<token>/<filename>', methods=['GET'])
|
||||||
@app.route('/download/<name>/<filename>', methods=['GET'])
|
@app.route('/download/<name>/<filename>', methods=['GET'])
|
||||||
def download_file(name,filename,password = None):
|
def download_file(name, filename, token = None):
|
||||||
if password != None:
|
(ok,share) = get_share(name, token = token)
|
||||||
session[name] = app.config['CRYPTO'].decrypt(password)
|
|
||||||
(ok,share) = get_share(name)
|
|
||||||
if not ok:
|
if not ok:
|
||||||
return share
|
return share
|
||||||
file_path = os.path.join(share['path'], filename)
|
file_path = os.path.join(share['path'], filename)
|
||||||
@@ -267,12 +262,10 @@ def download_file(name,filename,password = None):
|
|||||||
return send_from_directory(directory=share['path'], filename=filename)
|
return send_from_directory(directory=share['path'], filename=filename)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/zip/<name>/<password>', methods=['GET'])
|
@app.route('/zip/<name>/<token>', methods=['GET'])
|
||||||
@app.route('/zip/<name>', methods=['GET'])
|
@app.route('/zip/<name>', methods=['GET'])
|
||||||
def download_zip(name,password = None):
|
def download_zip(name, token = None):
|
||||||
if password != None:
|
(ok,share) = get_share(name, token = token)
|
||||||
session[name] = app.config['CRYPTO'].decrypt(password)
|
|
||||||
(ok,share) = get_share(name)
|
|
||||||
if not ok:
|
if not ok:
|
||||||
return share
|
return share
|
||||||
folder_size = get_folder_size(share['path'])
|
folder_size = get_folder_size(share['path'])
|
||||||
@@ -292,10 +285,9 @@ def download_zip(name,password = None):
|
|||||||
attachment_filename = name + ".zip"
|
attachment_filename = name + ".zip"
|
||||||
)
|
)
|
||||||
|
|
||||||
@app.route('/script/upload/<name>/<password>', methods=['GET'])
|
@app.route('/script/upload/<name>/<token>', methods=['GET'])
|
||||||
def script_upload(name = None, password = None):
|
def script_upload(name = None, token = None):
|
||||||
session[name] = app.config['CRYPTO'].decrypt(password)
|
(ok,share) = get_share(name, token = token)
|
||||||
(ok,share) = get_share(name)
|
|
||||||
if not ok:
|
if not ok:
|
||||||
return share
|
return share
|
||||||
if not get_or_none('upload', share) == True:
|
if not get_or_none('upload', share) == True:
|
||||||
@@ -335,14 +327,13 @@ done
|
|||||||
"""%(
|
"""%(
|
||||||
request.url_root,
|
request.url_root,
|
||||||
name,
|
name,
|
||||||
password
|
token
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/script/download/<name>/<password>', methods=['GET'])
|
@app.route('/script/download/<name>/<token>', methods=['GET'])
|
||||||
def script_download(name = None, password = None):
|
def script_download(name = None, token = None):
|
||||||
session[name] = app.config['CRYPTO'].decrypt(password)
|
(ok,share) = get_share(name, token = token)
|
||||||
(ok,share) = get_share(name)
|
|
||||||
if not ok:
|
if not ok:
|
||||||
return share
|
return share
|
||||||
files = []
|
files = []
|
||||||
@@ -385,7 +376,7 @@ get_file() {
|
|||||||
"""%(
|
"""%(
|
||||||
request.url_root,
|
request.url_root,
|
||||||
name,
|
name,
|
||||||
password
|
token
|
||||||
)
|
)
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
@@ -395,10 +386,9 @@ get_file() {
|
|||||||
return script
|
return script
|
||||||
|
|
||||||
|
|
||||||
@app.route('/script/direct/<name>/<password>', methods=['GET'])
|
@app.route('/script/direct/<name>/<token>', methods=['GET'])
|
||||||
def script_direct(name = None, password = None):
|
def script_direct(name = None, token = None):
|
||||||
session[name] = app.config['CRYPTO'].decrypt(password)
|
(ok,share) = get_share(name, token = token)
|
||||||
(ok,share) = get_share(name)
|
|
||||||
if not ok:
|
if not ok:
|
||||||
return share
|
return share
|
||||||
files = []
|
files = []
|
||||||
@@ -451,10 +441,9 @@ get_file() {
|
|||||||
return script
|
return script
|
||||||
|
|
||||||
|
|
||||||
@app.route('/script/upload_split/<name>/<password>', methods=['GET'])
|
@app.route('/script/upload_split/<name>/<token>', methods=['GET'])
|
||||||
def script_upload_split(name = None, password = None):
|
def script_upload_split(name = None, token = None):
|
||||||
session[name] = app.config['CRYPTO'].decrypt(password)
|
(ok,share) = get_share(name, token = token)
|
||||||
(ok,share) = get_share(name)
|
|
||||||
if not ok:
|
if not ok:
|
||||||
return share
|
return share
|
||||||
if not get_or_none('upload', share) == True:
|
if not get_or_none('upload', share) == True:
|
||||||
@@ -509,7 +498,7 @@ done
|
|||||||
"""%(
|
"""%(
|
||||||
request.url_root,
|
request.url_root,
|
||||||
name,
|
name,
|
||||||
password
|
token
|
||||||
)
|
)
|
||||||
|
|
||||||
class uploadJoiner:
|
class uploadJoiner:
|
||||||
@@ -554,7 +543,7 @@ def file_versionize(filename):
|
|||||||
os.rename(filename,new_name)
|
os.rename(filename,new_name)
|
||||||
|
|
||||||
|
|
||||||
def get_share(name, require_auth = True):
|
def get_share(name, require_auth = True, token = None):
|
||||||
share = [x for x in g.shares if x['name'] == name]
|
share = [x for x in g.shares if x['name'] == name]
|
||||||
if len(share) < 1:
|
if len(share) < 1:
|
||||||
return (False,redirect(url_for('authenticate',name=name)))
|
return (False,redirect(url_for('authenticate',name=name)))
|
||||||
@@ -562,6 +551,10 @@ def get_share(name, require_auth = True):
|
|||||||
if is_expired(share):
|
if is_expired(share):
|
||||||
return (False, 'Share has expired')
|
return (False, 'Share has expired')
|
||||||
authenticated = "no-pass"
|
authenticated = "no-pass"
|
||||||
|
if not token == None:
|
||||||
|
if has_token(token, share):
|
||||||
|
require_auth = False
|
||||||
|
authenticated = "token"
|
||||||
if require_auth:
|
if require_auth:
|
||||||
if 'pass_hash' in share:
|
if 'pass_hash' in share:
|
||||||
authenticated = False
|
authenticated = False
|
||||||
@@ -584,6 +577,7 @@ def get_share(name, require_auth = True):
|
|||||||
makedirs_rights(share['path'])
|
makedirs_rights(share['path'])
|
||||||
return (True,share)
|
return (True,share)
|
||||||
|
|
||||||
|
|
||||||
def is_expired(share):
|
def is_expired(share):
|
||||||
expires = get_or_none('expire', share)
|
expires = get_or_none('expire', share)
|
||||||
if expires:
|
if expires:
|
||||||
@@ -607,6 +601,7 @@ def makedirs_rights(path):
|
|||||||
os.mkdir(current_path)
|
os.mkdir(current_path)
|
||||||
set_rights(current_path)
|
set_rights(current_path)
|
||||||
|
|
||||||
|
|
||||||
def notify(msg):
|
def notify(msg):
|
||||||
if 'notifier' in app.config:
|
if 'notifier' in app.config:
|
||||||
msg['environment'] = request.environ
|
msg['environment'] = request.environ
|
||||||
|
|||||||
@@ -19,20 +19,20 @@ def get_root_path(opts):
|
|||||||
|
|
||||||
def list_shares(shares,opts):
|
def list_shares(shares,opts):
|
||||||
table = []
|
table = []
|
||||||
table.append(('Name', 'Path','Public','Password','PassHash','Upload','Overwrite','Direct','Expire','Recipient','Description'))
|
table.append(('Name', 'Path','Public','Password','Tokens','Upload','Overwrite','Direct','Expire','Recipient','Description'))
|
||||||
for share in shares:
|
for share in shares:
|
||||||
public = get_or_none('public',share, False)
|
public = get_or_none('public',share, False)
|
||||||
passhash = '-'
|
passtoken = '-'
|
||||||
password = 'pass_hash' in share
|
password = 'pass_hash' in share
|
||||||
if opts.show_password:
|
if opts.show_password:
|
||||||
if 'pass_plain' in share:
|
if 'pass_plain' in share:
|
||||||
password = share['pass_plain']
|
password = share['pass_plain']
|
||||||
else:
|
else:
|
||||||
password = ""
|
password = ""
|
||||||
if 'pass_hash' in share:
|
if 'tokens' in share:
|
||||||
passhash = share['pass_hash']
|
passtoken = ",".join(share['tokens'])
|
||||||
else:
|
else:
|
||||||
passhash = "-"
|
passtoken = ""
|
||||||
upload = get_or_none('upload',share, False)
|
upload = get_or_none('upload',share, False)
|
||||||
overwrite = get_or_none('overwrite',share, True)
|
overwrite = get_or_none('overwrite',share, True)
|
||||||
direct = get_or_none('direct_links',share, False) if password else False
|
direct = get_or_none('direct_links',share, False) if password else False
|
||||||
@@ -43,7 +43,7 @@ def list_shares(shares,opts):
|
|||||||
share['path']+"/",
|
share['path']+"/",
|
||||||
public,
|
public,
|
||||||
password,
|
password,
|
||||||
passhash,
|
passtoken,
|
||||||
upload,
|
upload,
|
||||||
overwrite,
|
overwrite,
|
||||||
direct,
|
direct,
|
||||||
@@ -102,6 +102,7 @@ def add_share(shares, config, opts):
|
|||||||
if opts.plain:
|
if opts.plain:
|
||||||
share['pass_plain'] = opts.password
|
share['pass_plain'] = opts.password
|
||||||
share['pass_hash'] = password_hash(opts.password, config['app_secret_key'])
|
share['pass_hash'] = password_hash(opts.password, config['app_secret_key'])
|
||||||
|
share['tokens'] = [random_token()]
|
||||||
if opts.expire:
|
if opts.expire:
|
||||||
try:
|
try:
|
||||||
date_object = datetime.strptime(opts.expire,"%Y-%m-%d %H:%M")
|
date_object = datetime.strptime(opts.expire,"%Y-%m-%d %H:%M")
|
||||||
@@ -138,7 +139,9 @@ def modify_share(shares, config, opts):
|
|||||||
for i,share in enumerate(shares):
|
for i,share in enumerate(shares):
|
||||||
if share['name'] != opts.name:
|
if share['name'] != opts.name:
|
||||||
continue
|
continue
|
||||||
orig_share = share.copy()
|
orig_share = dict(share)
|
||||||
|
if 'tokens' in share:
|
||||||
|
orig_share['tokens'] = list(share['tokens'])
|
||||||
print(json.dumps(share, indent = 2, sort_keys = True))
|
print(json.dumps(share, indent = 2, sort_keys = True))
|
||||||
found = True
|
found = True
|
||||||
break
|
break
|
||||||
@@ -167,6 +170,19 @@ def modify_share(shares, config, opts):
|
|||||||
share['pass_plain'] = opts.password
|
share['pass_plain'] = opts.password
|
||||||
share['pass_hash'] = password_hash(opts.password, config['app_secret_key'])
|
share['pass_hash'] = password_hash(opts.password, config['app_secret_key'])
|
||||||
|
|
||||||
|
# Handle tokens
|
||||||
|
if opts.remove_tokens:
|
||||||
|
for token in opts.remove_tokens:
|
||||||
|
if token in share['tokens']:
|
||||||
|
share['tokens'].remove(token)
|
||||||
|
if opts.tokens:
|
||||||
|
for token in opts.tokens:
|
||||||
|
if not 'tokens' in share:
|
||||||
|
share['tokens'] = []
|
||||||
|
if not token in share['tokens']:
|
||||||
|
share['tokens'].append(token)
|
||||||
|
|
||||||
|
|
||||||
if opts.expire:
|
if opts.expire:
|
||||||
if opts.expire == "":
|
if opts.expire == "":
|
||||||
# REMOVE EXPIRATION
|
# REMOVE EXPIRATION
|
||||||
@@ -201,7 +217,7 @@ def modify_share(shares, config, opts):
|
|||||||
if not key in orig_share:
|
if not key in orig_share:
|
||||||
modified.append(key)
|
modified.append(key)
|
||||||
continue
|
continue
|
||||||
if orig_share[key] != share[key]:
|
if str(orig_share[key]) != str(share[key]):
|
||||||
modified.append(key)
|
modified.append(key)
|
||||||
continue
|
continue
|
||||||
for key in orig_share:
|
for key in orig_share:
|
||||||
@@ -242,109 +258,161 @@ def print_rest_api(shares, config, opts):
|
|||||||
share = shares[0]
|
share = shares[0]
|
||||||
|
|
||||||
if opts.type == "list":
|
if opts.type == "list":
|
||||||
print("Link to enter the share:")
|
print_rest_api_list(config,share)
|
||||||
print("%s/list/%s"%(
|
|
||||||
config['public_url'],
|
|
||||||
share['name']
|
|
||||||
))
|
|
||||||
return
|
return
|
||||||
if not 'pass_hash' in share:
|
|
||||||
print("REST API enabled only if pass_hash is set for share")
|
|
||||||
sys.exit(1)
|
|
||||||
crypter = Crypto(config['app_secret_key'])
|
|
||||||
crypted = crypter.encrypt(share['pass_hash'])
|
|
||||||
if opts.type == "login":
|
|
||||||
print("Link to automatically login in the share:")
|
|
||||||
print("%s/list/%s/%s"%(
|
|
||||||
config['public_url'],
|
|
||||||
share['name'],
|
|
||||||
crypted
|
|
||||||
))
|
|
||||||
elif opts.type == "upload":
|
|
||||||
if 'upload' not in share or not share['upload']:
|
|
||||||
print("Uploading not allowed to this share")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
print("Link to upload file to the share:")
|
if (not 'tokens' in share) or len(share['tokens']) == 0:
|
||||||
print("\n# curl -F file=@'the_file_name.ext' %s/upload/%s/%s"%(
|
print("REST API enabled only if tokens are set for the share")
|
||||||
config['public_url'],
|
sys.exit(1)
|
||||||
share['name'],
|
token = False
|
||||||
crypted
|
if len(share['tokens']) == 1:
|
||||||
))
|
token = share['tokens'][0]
|
||||||
print("\nLink to upload multiple files to the share:")
|
else:
|
||||||
print("\n# curl -s %s/script/upload/%s/%s | bash /dev/stdin file_to_upload.ext [second.file.ext]"%(
|
try:
|
||||||
config['public_url'],
|
token_int = int(opts.token) - 1
|
||||||
share['name'],
|
if token_int < 0:
|
||||||
crypted
|
raise ValueError
|
||||||
))
|
token = share['tokens'][token_int]
|
||||||
print("\nLink to upload multiple files to the share, splitting large files (default 512Mb):")
|
except (IndexError, ValueError, TypeError) as e:
|
||||||
print("\n# curl -s %s/script/upload_split/%s/%s | bash /dev/stdin [-s split_size_in_Mb] file_to_upload.ext [second.file.ext]"%(
|
if opts.token in share['tokens']:
|
||||||
config['public_url'],
|
token = opts.token
|
||||||
share['name'],
|
if not token:
|
||||||
crypted
|
# more tokens!
|
||||||
))
|
|
||||||
|
if opts.token:
|
||||||
|
print("No such token for this share")
|
||||||
|
print("Tokens:")
|
||||||
|
for i,token in enumerate(share['tokens']):
|
||||||
|
print("%d. %s"%( i+1, token ))
|
||||||
|
print("Run again with --token [nr]")
|
||||||
|
if not opts.token:
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
sys.exit(1)
|
||||||
|
if opts.type == "login":
|
||||||
|
print_rest_api_login(config,share,token)
|
||||||
|
elif opts.type == "upload":
|
||||||
|
print_rest_api_upload(config,share,token)
|
||||||
elif opts.type == "download":
|
elif opts.type == "download":
|
||||||
print("Links to download files:")
|
print_rest_api_download(config, share, token)
|
||||||
share_path = os.path.join(
|
|
||||||
config['__root_path__'],
|
|
||||||
config['data_folder'],
|
|
||||||
share['path']
|
|
||||||
)
|
|
||||||
if not os.path.exists(share_path):
|
|
||||||
print("no files")
|
|
||||||
sys.exit(0)
|
|
||||||
for filename in sorted(os.listdir(share_path)):
|
|
||||||
if os.path.isdir(os.path.join(share_path,filename)):
|
|
||||||
continue
|
|
||||||
if filename.startswith("."):
|
|
||||||
continue
|
|
||||||
print("%s/download/%s/%s/%s"%(
|
|
||||||
config['public_url'],
|
|
||||||
share['name'],
|
|
||||||
crypted,
|
|
||||||
filename
|
|
||||||
))
|
|
||||||
print("or \n\n# curl -s %s/script/download/%s/%s | bash /dev/stdin [-f]"%(
|
|
||||||
config['public_url'],
|
|
||||||
share['name'],
|
|
||||||
crypted
|
|
||||||
))
|
|
||||||
elif opts.type == "direct":
|
elif opts.type == "direct":
|
||||||
if 'direct_links' not in share or not share['direct_links']:
|
print_rest_api_direct(config, share, token)
|
||||||
print("Direct downloading not allowed in this share")
|
|
||||||
sys.exit(0)
|
|
||||||
print("Links to direct download files:")
|
|
||||||
share_path = os.path.join(
|
|
||||||
config['__root_path__'],
|
|
||||||
config['data_folder'],
|
|
||||||
share['path']
|
|
||||||
)
|
|
||||||
if not os.path.exists(share_path):
|
|
||||||
print("no files")
|
|
||||||
sys.exit(0)
|
|
||||||
for filename in sorted(os.listdir(share_path)):
|
|
||||||
if os.path.isdir(os.path.join(share_path,filename)):
|
|
||||||
continue
|
|
||||||
if filename.startswith("."):
|
|
||||||
continue
|
|
||||||
print("%s/direct/%s/%s/%s"%(
|
|
||||||
config['public_url'],
|
|
||||||
share['name'],
|
|
||||||
get_direct_token(share,filename),
|
|
||||||
filename
|
|
||||||
))
|
|
||||||
print("or \n\n# curl -s %s/script/direct/%s/%s | bash /dev/stdin [-f]"%(
|
|
||||||
config['public_url'],
|
|
||||||
share['name'],
|
|
||||||
crypted
|
|
||||||
))
|
|
||||||
elif opts.type == "zip":
|
elif opts.type == "zip":
|
||||||
print("ZIP download:")
|
print_rest_api_zip(config, share, token)
|
||||||
print("%s/zip/%s/%s"%(
|
|
||||||
|
|
||||||
|
def print_rest_api_list(config, share):
|
||||||
|
print("Link to enter the share:")
|
||||||
|
print("%s/list/%s"%(
|
||||||
|
config['public_url'],
|
||||||
|
share['name']
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def print_rest_api_login(config, share, token):
|
||||||
|
print("Link to automatically login in the share:")
|
||||||
|
print("%s/list/%s/%s"%(
|
||||||
|
config['public_url'],
|
||||||
|
share['name'],
|
||||||
|
token
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def print_rest_api_upload(config, share, token):
|
||||||
|
if 'upload' not in share or not share['upload']:
|
||||||
|
print("Uploading not allowed to this share")
|
||||||
|
sys.exit(0)
|
||||||
|
print("Link to upload file to the share:")
|
||||||
|
print("\n# curl -F file=@'the_file_name.ext' %s/upload/%s/%s"%(
|
||||||
|
config['public_url'],
|
||||||
|
share['name'],
|
||||||
|
token
|
||||||
|
))
|
||||||
|
print("\nLink to upload multiple files to the share:")
|
||||||
|
print("\n# curl -s %s/script/upload/%s/%s | bash /dev/stdin file_to_upload.ext [second.file.ext]"%(
|
||||||
|
config['public_url'],
|
||||||
|
share['name'],
|
||||||
|
token
|
||||||
|
))
|
||||||
|
print("\nLink to upload multiple files to the share, splitting large files (default 512Mb):")
|
||||||
|
print("\n# curl -s %s/script/upload_split/%s/%s | bash /dev/stdin [-s split_size_in_Mb] file_to_upload.ext [second.file.ext]"%(
|
||||||
|
config['public_url'],
|
||||||
|
share['name'],
|
||||||
|
token
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def print_rest_api_download(config, share, token):
|
||||||
|
print("Links to download files:")
|
||||||
|
share_path = os.path.join(
|
||||||
|
config['__root_path__'],
|
||||||
|
config['data_folder'],
|
||||||
|
share['path']
|
||||||
|
)
|
||||||
|
if not os.path.exists(share_path):
|
||||||
|
print("no files")
|
||||||
|
sys.exit(0)
|
||||||
|
for filename in sorted(os.listdir(share_path)):
|
||||||
|
if os.path.isdir(os.path.join(share_path,filename)):
|
||||||
|
continue
|
||||||
|
if filename.startswith("."):
|
||||||
|
continue
|
||||||
|
print("%s/download/%s/%s/%s"%(
|
||||||
config['public_url'],
|
config['public_url'],
|
||||||
share['name'],
|
share['name'],
|
||||||
crypted
|
token,
|
||||||
|
filename
|
||||||
))
|
))
|
||||||
|
print("or \n\n# curl -s %s/script/download/%s/%s | bash /dev/stdin [-f]"%(
|
||||||
|
config['public_url'],
|
||||||
|
share['name'],
|
||||||
|
token
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def print_rest_api_direct(config, share, token):
|
||||||
|
if 'direct_links' not in share or not share['direct_links']:
|
||||||
|
print("Direct downloading not allowed in this share")
|
||||||
|
sys.exit(0)
|
||||||
|
print("Links to direct download files:")
|
||||||
|
share_path = os.path.join(
|
||||||
|
config['__root_path__'],
|
||||||
|
config['data_folder'],
|
||||||
|
share['path']
|
||||||
|
)
|
||||||
|
if not os.path.exists(share_path):
|
||||||
|
print("no files")
|
||||||
|
sys.exit(0)
|
||||||
|
for filename in sorted(os.listdir(share_path)):
|
||||||
|
if os.path.isdir(os.path.join(share_path,filename)):
|
||||||
|
continue
|
||||||
|
if filename.startswith("."):
|
||||||
|
continue
|
||||||
|
print("%s/direct/%s/%s/%s"%(
|
||||||
|
config['public_url'],
|
||||||
|
share['name'],
|
||||||
|
get_direct_token(share,filename),
|
||||||
|
filename
|
||||||
|
))
|
||||||
|
print("or \n\n# curl -s %s/script/direct/%s/%s | bash /dev/stdin [-f]"%(
|
||||||
|
config['public_url'],
|
||||||
|
share['name'],
|
||||||
|
token
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def print_rest_api_zip(config, share, token):
|
||||||
|
print("ZIP download:")
|
||||||
|
print("%s/zip/%s/%s"%(
|
||||||
|
config['public_url'],
|
||||||
|
share['name'],
|
||||||
|
token
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def print_token():
|
||||||
|
print(random_token())
|
||||||
|
|
||||||
|
|
||||||
def parse_options():
|
def parse_options():
|
||||||
@@ -431,6 +499,12 @@ def parse_options():
|
|||||||
parser_modify.add_argument('-r','--recipient', action="store", dest="recipient", default = None,
|
parser_modify.add_argument('-r','--recipient', action="store", dest="recipient", default = None,
|
||||||
help= "Recipient for notifications (if enabled)"
|
help= "Recipient for notifications (if enabled)"
|
||||||
)
|
)
|
||||||
|
parser_modify.add_argument('-t','--add-token', action="append", dest="tokens", default = [],
|
||||||
|
help= "Token for REST api, may be issued multiple times"
|
||||||
|
)
|
||||||
|
parser_modify.add_argument('--remove-token', action="append", dest="remove_tokens", default = [],
|
||||||
|
help= "Remove REST tokens, may be issued multiple times"
|
||||||
|
)
|
||||||
parser_modify.add_argument('-w','--write', action="store_true", dest="write", default = False,
|
parser_modify.add_argument('-w','--write', action="store_true", dest="write", default = False,
|
||||||
help = "Write changes to the shares.json file"
|
help = "Write changes to the shares.json file"
|
||||||
)
|
)
|
||||||
@@ -440,6 +514,11 @@ def parse_options():
|
|||||||
parser_rest.add_argument(dest="type", help = "Type of command",
|
parser_rest.add_argument(dest="type", help = "Type of command",
|
||||||
choices = ['list','login','upload','download','direct','zip']
|
choices = ['list','login','upload','download','direct','zip']
|
||||||
)
|
)
|
||||||
|
parser_rest.add_argument('-t','--token', action="store", dest="token", default = None,
|
||||||
|
help= "If share has multiple tokens, select one to print REST API for."
|
||||||
|
)
|
||||||
|
## TOKEN
|
||||||
|
parser_token = subparsers.add_parser('token', help = "Generate a random token")
|
||||||
|
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
@@ -477,6 +556,8 @@ if __name__ == "__main__":
|
|||||||
modify_share(shares,config,opts)
|
modify_share(shares,config,opts)
|
||||||
elif opts.subparser_name == 'rest':
|
elif opts.subparser_name == 'rest':
|
||||||
print_rest_api(shares,config,opts)
|
print_rest_api(shares,config,opts)
|
||||||
|
elif opts.subparser_name == 'token':
|
||||||
|
print_token()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import random
|
||||||
|
import string
|
||||||
import base64
|
import base64
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
import hashlib
|
import hashlib
|
||||||
@@ -44,6 +46,12 @@ def get_direct_token(share, filename):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def has_token(token, share):
|
||||||
|
if not 'tokens' in share:
|
||||||
|
return False
|
||||||
|
return token in share['tokens']
|
||||||
|
|
||||||
|
|
||||||
def password_hash(string, salt=""):
|
def password_hash(string, salt=""):
|
||||||
if type(string) == str:
|
if type(string) == str:
|
||||||
string = string.encode("utf-8")
|
string = string.encode("utf-8")
|
||||||
@@ -54,6 +62,12 @@ def password_hash(string, salt=""):
|
|||||||
).hexdigest()
|
).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def random_token():
|
||||||
|
chars = [random.choice(string.ascii_letters + string.digits) for n in range(30)]
|
||||||
|
token = "".join(chars)
|
||||||
|
return token
|
||||||
|
|
||||||
|
|
||||||
def remove_pad(string):
|
def remove_pad(string):
|
||||||
""" Remove spaces from right """
|
""" Remove spaces from right """
|
||||||
return string.rstrip(" ")
|
return string.rstrip(" ")
|
||||||
|
|||||||
Reference in New Issue
Block a user