password protection for web pages
This commit is contained in:
@@ -1,13 +1,18 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# coding=utf-8
|
||||
''' A script that creates an index for a folder.
|
||||
'''
|
||||
|
||||
import os,sys,time
|
||||
import urllib
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import string
|
||||
import urllib.parse
|
||||
from glob import fnmatch
|
||||
import base64
|
||||
import random
|
||||
|
||||
VERSION = "20200427"
|
||||
VERSION = "20200619"
|
||||
IMAGE_EXTENSIONS = ['png', 'gif', 'jpg', 'jpeg', 'tif', 'tiff']
|
||||
|
||||
def setup():
|
||||
@@ -26,6 +31,8 @@ def setup():
|
||||
help="Output filename (Default: index.html)")
|
||||
parser.add_argument("-p",action="store_false",dest="parent",default=True,
|
||||
help="Do no print .. link for parent folder.")
|
||||
parser.add_argument("--password",type=str,dest="password",default=None,
|
||||
help="Set a password to view page. The file list will be written to a randomly generated filename. Note: this is not secure, as the target link will still be unprotected if known.")
|
||||
parser.add_argument("-r",action="store_true",dest="recursive",default=False,
|
||||
help="Include all files recursively in the list. Do not include any folders.")
|
||||
parser.add_argument("--images",action="store_true",dest="images",default=False,
|
||||
@@ -57,7 +64,7 @@ def setup2HTML(opts):
|
||||
return '<meta name="SimpleWebPageSetup" content="%s"/>'%";".join([
|
||||
'hidden=%s'%opts.hidden,
|
||||
'parent=%s'%opts.parent,
|
||||
'title=%s'%urllib.quote(opts.title),
|
||||
'title=%s'%urllib.parse.quote(opts.title),
|
||||
'images=%s'%opts.images
|
||||
])
|
||||
|
||||
@@ -73,7 +80,7 @@ def HTML2setup(opts):
|
||||
(k,v) = s.split('=',1)
|
||||
if k == 'hidden': opts.hidden = v == "True"
|
||||
if k == 'parent': opts.parent = v == "True"
|
||||
if k == 'title': opts.title = urllib.unquote(v)
|
||||
if k == 'title': opts.title = urllib.parse.unquote(v)
|
||||
if k == 'images': opts.images = v == "True"
|
||||
read_config = True
|
||||
print("Reading options from existing " + opts.filename)
|
||||
@@ -109,7 +116,11 @@ def get_files_and_folders(opts):
|
||||
|
||||
|
||||
def generate_index(opts):
|
||||
opts.password_filename = ""
|
||||
dirs, files, path = get_files_and_folders(opts)
|
||||
if opts.password != None:
|
||||
opts.password_filename = opts.filename
|
||||
opts.filename = generate_password_page(path, opts.filename, opts.password)
|
||||
existing_config = False
|
||||
if opts.filename in files:
|
||||
opts, existing_config = HTML2setup(opts)
|
||||
@@ -117,6 +128,7 @@ def generate_index(opts):
|
||||
print(opts.filename + " exists, and not generated with SimpleWebPage. Exiting.")
|
||||
sys.exit(1)
|
||||
files = [ f for f in files if f != opts.filename]
|
||||
files = [ f for f in files if f != opts.password_filename]
|
||||
files = match_files(files, opts.includes)
|
||||
dirs.sort()
|
||||
files.sort()
|
||||
@@ -135,6 +147,50 @@ def generate_index(opts):
|
||||
return
|
||||
|
||||
|
||||
def generate_password_page(path, password_file, password):
|
||||
def ha(p):
|
||||
h = 0
|
||||
for c in p:
|
||||
h = ((h<<5) - h) + ord(c)
|
||||
h = h & 0xffffffff
|
||||
if h > (1<<31) - 1:
|
||||
h -= (1<<32)
|
||||
return h
|
||||
|
||||
|
||||
def scramble(p,t):
|
||||
p = (p * (int(len(t)/len(p))+1))[:len(t)]
|
||||
t = (t * (int(len(p)/len(t))+1))[:len(p)]
|
||||
s = ''
|
||||
for i in range(len(p)):
|
||||
s += chr(ord(p[i]) + ord(t[i]))
|
||||
return s
|
||||
|
||||
|
||||
def enc(s):
|
||||
return base64.b64encode(s.encode('latin1')).decode('ascii')
|
||||
|
||||
|
||||
def random_string(stringLength=8):
|
||||
letters = string.ascii_lowercase + string.digits
|
||||
return ''.join(random.choice(letters) for i in range(stringLength))
|
||||
|
||||
|
||||
def get_target(filename):
|
||||
return "{0}.{2}{1}".format(
|
||||
*os.path.splitext(filename) + (random_string(),)
|
||||
)
|
||||
|
||||
target_file = get_target(password_file)
|
||||
secret = "{}:{}".format(
|
||||
ha(password),
|
||||
enc(scramble(password, target_file))
|
||||
)
|
||||
with open(os.path.join(path, password_file), 'wt') as f:
|
||||
f.write(get_password_page(secret))
|
||||
return target_file
|
||||
|
||||
|
||||
def get_filelink(path,fname,images=False):
|
||||
if os.path.islink(os.path.join(path, fname)) and not os.path.exists(os.path.join(path, fname)):
|
||||
(fsize, fsstr, fsstrb, fdstr)=(0, "NA", "NA", "NA")
|
||||
@@ -149,7 +205,7 @@ def get_filelink(path,fname,images=False):
|
||||
else:
|
||||
fname_str = fname
|
||||
return '<tr><td><a class="link_file" href="%s">%s</a><td class="right">%s</td><td class="right bytes">%s</td><td class="right">%s</td></tr>\n'%(
|
||||
urllib.quote(fname),
|
||||
urllib.parse.quote(fname),
|
||||
fname_str,
|
||||
fsstr,
|
||||
fsstrb,
|
||||
@@ -160,7 +216,7 @@ def get_filelink(path,fname,images=False):
|
||||
def get_wget_lines(files):
|
||||
wget = "\n<!--\n"
|
||||
for f in files:
|
||||
wget += "#FILE %s\n"%(urllib.quote(f),)
|
||||
wget += "#FILE %s\n"%(urllib.parse.quote(f),)
|
||||
wget += "#WGET URL=[insert URL] && curl $URL | grep '^#FILE ' | cut -c7- | sed \"s,^,$URL,\" | xargs -n1 wget\n"
|
||||
wget += "-->\n"
|
||||
return wget
|
||||
@@ -168,7 +224,7 @@ def get_wget_lines(files):
|
||||
|
||||
def get_imagestr(fname):
|
||||
return '<img src="%s" title="%s"/>'%(
|
||||
urllib.quote(fname),
|
||||
urllib.parse.quote(fname),
|
||||
fname
|
||||
)
|
||||
|
||||
@@ -176,11 +232,102 @@ def get_pathlink(path,dname):
|
||||
fdate = time.localtime(os.path.getmtime(os.path.join(path, dname)))
|
||||
fdstr = time.strftime("%Y/%m/%d %H:%M:%S", fdate)
|
||||
return '<tr><td><a class="link_dir" href="%s">↳ %s/</a><td class="right">[DIR]</td><td class="right bytes">0</td><td class="right">%s</td></tr>\n'%(
|
||||
urllib.quote(dname),
|
||||
urllib.parse.quote(dname),
|
||||
dname,
|
||||
fdstr
|
||||
)
|
||||
|
||||
|
||||
def get_password_page(secret):
|
||||
return """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
|
||||
<title>Password required</title>
|
||||
<script language="JavaScript">
|
||||
Object.defineProperty(String.prototype, 'hashCode', {
|
||||
value: function() {
|
||||
var hash = 0, i, chr;
|
||||
for (i = 0; i < this.length; i++) {
|
||||
chr = this.charCodeAt(i);
|
||||
hash = ((hash << 5) - hash) + chr;
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
});
|
||||
function scramble(p,t) {
|
||||
p = p.padEnd(t.length, p);
|
||||
t = t.padEnd(p.length,' ');
|
||||
var sstr = '';
|
||||
for (var i=0; i < t.length; i++) {
|
||||
sstr += String.fromCharCode(p.charCodeAt(i) + t.charCodeAt(i));
|
||||
}
|
||||
return sstr;
|
||||
}
|
||||
function unscramble(p,t) {
|
||||
p = p.padEnd(t.length, p);
|
||||
t = t.padEnd(p.length, ' ');
|
||||
var sstr = '';
|
||||
for (var i=0; i < t.length; i++) {
|
||||
sstr += String.fromCharCode(t.charCodeAt(i) - p.charCodeAt(i));
|
||||
}
|
||||
return sstr;
|
||||
}
|
||||
function pw_event(element) {
|
||||
if(event.key === 'Enter') {
|
||||
pw(element);
|
||||
}
|
||||
}
|
||||
function pw(element) {
|
||||
var pw = "SECRET";
|
||||
if (element.value) {
|
||||
pw = pw.split(':',2);
|
||||
if (element.value.hashCode() == parseInt(pw[0])) {
|
||||
this.location.href = unscramble(element.value, atob(pw[1]));
|
||||
} else {
|
||||
document.getElementById("message").innerHTML = "No match";
|
||||
setTimeout(clear_message, 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
function clear_message() {
|
||||
document.getElementById("message").innerHTML = '';
|
||||
}
|
||||
function newPass(password, target) {
|
||||
console.log(
|
||||
' var pw = "' + password.hashCode().toString() + ':' +
|
||||
btoa(scramble(password, target)) + '";'
|
||||
);
|
||||
}
|
||||
//console.log("Use newPass('pass','page.html'); to get a new hash");
|
||||
</script>
|
||||
<style>
|
||||
#content {
|
||||
text-align: center;
|
||||
padding-top: 4rem;
|
||||
font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
#message {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
#button {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<input type="password" placeholder="Password" class="password" onkeydown="pw_event(this)" id="password" onFocus="this.select()" autofocus/>
|
||||
<br><button id="button" type="button" onclick="pw(document.getElementById('password'));">Enter</button>
|
||||
<div id="message"></div>
|
||||
<noscript><br>Javascript is required to access this area. Yours seems to be disabled.</noscript>
|
||||
</div>
|
||||
</body>
|
||||
</html>""".replace("SECRET",secret)
|
||||
|
||||
def get_header(opts):
|
||||
header='''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
||||
<html>
|
||||
|
||||
Reference in New Issue
Block a user