adding password encryption to increase security

This commit is contained in:
Andrew Davidson 2014-12-28 16:50:25 -08:00
parent 041b490e7f
commit 023eb1529e

141
bookie.py
View file

@ -9,6 +9,7 @@ import urllib
from subprocess import call from subprocess import call
from bs4 import BeautifulSoup as BS from bs4 import BeautifulSoup as BS
from xml.sax.saxutils import escape from xml.sax.saxutils import escape
from flask.ext.security.utils import encrypt_password
app = Flask(__name__) app = Flask(__name__)
@ -19,7 +20,8 @@ app.config["MONGODB_DB"] = "bookie"
app.config['SECRET_KEY'] = 'bobloblawlawblog' app.config['SECRET_KEY'] = 'bobloblawlawblog'
app.config['UPLOAD_FOLDER'] = "static/uploads" app.config['UPLOAD_FOLDER'] = "static/uploads"
app.config['SITE_URL'] = "http://localhost:5000" app.config['SITE_URL'] = "http://localhost:5000"
app.config['SECURITY_PASSWORD_HASH'] = "bcrypt"
app.config['SECURITY_PASSWORD_SALT'] = "asdfiqwnvonaosinva"
##### #####
# MongoDB Setup # MongoDB Setup
@ -42,6 +44,9 @@ class User(db.Document, UserMixin):
confirmed_at = db.DateTimeField() confirmed_at = db.DateTimeField()
roles = db.ListField(db.ReferenceField(Role), default=[]) roles = db.ListField(db.ReferenceField(Role), default=[])
def encrypt_password(self, pw):
return encrypt_password(pw)
class Tag(db.Document): class Tag(db.Document):
name = db.StringField(required=True, max_length=25, unique=True) name = db.StringField(required=True, max_length=25, unique=True)
note = db.StringField(required=False, max_length=100) note = db.StringField(required=False, max_length=100)
@ -56,7 +61,7 @@ class ArchivedText(db.Document):
created_at = db.DateTimeField(default=datetime.datetime.now, required=True) created_at = db.DateTimeField(default=datetime.datetime.now, required=True)
text = db.StringField(required=True,default="") text = db.StringField(required=True,default="")
raw_html = db.StringField(required=True,default="") raw_html = db.StringField(required=True,default="")
def get_html(self): def get_html(self):
app.logger.debug("Brewing an opener") app.logger.debug("Brewing an opener")
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor()) opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor())
@ -69,7 +74,7 @@ class ArchivedText(db.Document):
return raw_html.decode() return raw_html.decode()
except: except:
return str(raw_html) return str(raw_html)
class ArchivedImage(db.Document): class ArchivedImage(db.Document):
url = db.StringField(max_length=1000, required=True) url = db.StringField(max_length=1000, required=True)
created_at = db.DateTimeField(default=datetime.datetime.now, required=True) created_at = db.DateTimeField(default=datetime.datetime.now, required=True)
@ -98,13 +103,13 @@ class Bookmark(db.Document):
#Metrics #Metrics
hits = db.IntField(required=True,default=0) hits = db.IntField(required=True,default=0)
factor = db.FloatField(required=True) factor = db.FloatField(required=True)
def get_factor(self): def get_factor(self):
return (len(self.short)+14)/len(self.url) return (len(self.short)+14)/len(self.url)
def get_short(self): def get_short(self):
unique = False unique = False
while not unique: while not unique:
s = base64.urlsafe_b64encode(os.urandom(5))[0:5].decode('Latin-1') s = base64.urlsafe_b64encode(os.urandom(5))[0:5].decode('Latin-1')
if Bookmark.objects(short=s).first() == None: if Bookmark.objects(short=s).first() == None:
unique = True unique = True
@ -115,7 +120,7 @@ class Bookmark(db.Document):
'indexes': ['-created_at', 'short'], 'indexes': ['-created_at', 'short'],
'ordering': ['-created_at'] 'ordering': ['-created_at']
} }
def __repr__(self): def __repr__(self):
return "Bookmark()" return "Bookmark()"
def __str__(self): def __str__(self):
@ -175,8 +180,8 @@ def update_archived_image(b):
return True return True
# A custom function to extract the key test from the raw html # A custom function to extract the key test from the raw html
# Inputs: # Inputs:
# Outputs: # Outputs:
def html_parse(raw_html,url,paragraphs=True): def html_parse(raw_html,url,paragraphs=True):
strip_tags = False strip_tags = False
soup = BS(raw_html) soup = BS(raw_html)
@ -191,19 +196,19 @@ def html_parse(raw_html,url,paragraphs=True):
text = soup.find("div", attrs={"class":"story-text"}) text = soup.find("div", attrs={"class":"story-text"})
elif soup.find("div", attrs={"id":"article"}): elif soup.find("div", attrs={"id":"article"}):
app.logger.debug('Text import from <div id="article">') app.logger.debug('Text import from <div id="article">')
text = soup.find("div", attrs={"id":"article"}) text = soup.find("div", attrs={"id":"article"})
elif soup.find("div", attrs={"id":"articleBody"}): elif soup.find("div", attrs={"id":"articleBody"}):
app.logger.debug('Text import from <div id="articleBody">') app.logger.debug('Text import from <div id="articleBody">')
text = soup.find("div", attrs={"id":"articleBody"}) text = soup.find("div", attrs={"id":"articleBody"})
elif soup.find("div", attrs={"class":"articleBody"}): elif soup.find("div", attrs={"class":"articleBody"}):
app.logger.debug('Text import from <div class="articleBody">') app.logger.debug('Text import from <div class="articleBody">')
text = soup.find("div", attrs={"class":"articleBody"}) text = soup.find("div", attrs={"class":"articleBody"})
elif soup.find("div", attrs={"class":"post"}): elif soup.find("div", attrs={"class":"post"}):
app.logger.debug('Text import from <div class="post">') app.logger.debug('Text import from <div class="post">')
text = soup.find("div", attrs={"class":"post"}) text = soup.find("div", attrs={"class":"post"})
elif soup.find("div", attrs={"class":"post-content"}): elif soup.find("div", attrs={"class":"post-content"}):
app.logger.debug('Text import from <div class="post-content">') app.logger.debug('Text import from <div class="post-content">')
text = soup.find("div", attrs={"class":"post-content"}) text = soup.find("div", attrs={"class":"post-content"})
elif soup.find("div", attrs={"class":"article-content"}): elif soup.find("div", attrs={"class":"article-content"}):
app.logger.debug('Text import from <div class="article-content">') app.logger.debug('Text import from <div class="article-content">')
text = soup.find("div", attrs={"class":"article-content"}) text = soup.find("div", attrs={"class":"article-content"})
@ -218,12 +223,12 @@ def html_parse(raw_html,url,paragraphs=True):
text = soup.find("article") text = soup.find("article")
elif soup.find("div", attrs={"id":"page"}): elif soup.find("div", attrs={"id":"page"}):
app.logger.debug('Text import from <div id="page">') app.logger.debug('Text import from <div id="page">')
text = soup.find("div", attrs={"id":"page"}) text = soup.find("div", attrs={"id":"page"})
else: else:
app.logger.debug('Text import from <body>') app.logger.debug('Text import from <body>')
text = soup("body")[0] text = soup("body")[0]
strip_tags = True strip_tags = True
if paragraphs == True: if paragraphs == True:
for t in text('img'): for t in text('img'):
t['style'] = "max-width:600px;max-height:600px;" t['style'] = "max-width:600px;max-height:600px;"
@ -237,8 +242,8 @@ def html_parse(raw_html,url,paragraphs=True):
for t in text("iframe"): for t in text("iframe"):
del(t['height']) del(t['height'])
del(t['width']) del(t['width'])
t['style'] = "max-width:600px;max-height:600px;margin:0em auto;display:block;" t['style'] = "max-width:600px;max-height:600px;margin:0em auto;display:block;"
if strip_tags == True: if strip_tags == True:
lines = (line.strip() for line in text.get_text().splitlines()) lines = (line.strip() for line in text.get_text().splitlines())
chunks = (phrase.strip() for line in lines for phrase in line.split(" ")) chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
@ -251,7 +256,7 @@ def html_parse(raw_html,url,paragraphs=True):
lines = (line.strip() for line in text.get_text().splitlines()) lines = (line.strip() for line in text.get_text().splitlines())
chunks = (phrase.strip() for line in lines for phrase in line.split(" ")) chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
output = '\n'.join(chunk for chunk in chunks if chunk) output = '\n'.join(chunk for chunk in chunks if chunk)
return output return output
# A function defining key banned HTML tags # A function defining key banned HTML tags
@ -264,10 +269,10 @@ def kill_list():
kill_list.append(["div", {"class": "m-linkset"}]) kill_list.append(["div", {"class": "m-linkset"}])
kill_list.append(["div", {"class": "m-feature__intro"}]) kill_list.append(["div", {"class": "m-feature__intro"}])
kill_list.append(["div", {"class": "m-share-buttons"}]) kill_list.append(["div", {"class": "m-share-buttons"}])
kill_list.append(["p", {"class": "m-entry__byline"}]) kill_list.append(["p", {"class": "m-entry__byline"}])
kill_list.append(["div", {"class": "social"}]) kill_list.append(["div", {"class": "social"}])
kill_list.append(["div", {"id": "follow-bar"}]) kill_list.append(["div", {"id": "follow-bar"}])
kill_list.append(["section", {"class": "m-rail-component"}]) kill_list.append(["section", {"class": "m-rail-component"}])
return kill_list return kill_list
@ -276,22 +281,22 @@ def kill_list():
# Inputs: mongoengine object or query # Inputs: mongoengine object or query
# Outputs: Prepared instance for JSON dump # Outputs: Prepared instance for JSON dump
def encode_model(self, obj): def encode_model(self, obj):
if isinstance(obj, (mongoengine.Document, mongoengine.EmbeddedDocument)): if isinstance(obj, (mongoengine.Document, mongoengine.EmbeddedDocument)):
out = dict(obj._data) out = dict(obj._data)
for k,v in out.items(): for k,v in out.items():
if isinstance(v, ObjectId): if isinstance(v, ObjectId):
out[k] = str(v) out[k] = str(v)
elif isinstance(obj, mongoengine.queryset.QuerySet): elif isinstance(obj, mongoengine.queryset.QuerySet):
out = list(obj) out = list(obj)
elif isinstance(obj, types.ModuleType): elif isinstance(obj, types.ModuleType):
out = None out = None
elif isinstance(obj, groupby): elif isinstance(obj, groupby):
out = [ (g,list(l)) for g,l in obj ] out = [ (g,list(l)) for g,l in obj ]
else: else:
raise TypeError("Could not JSON-encode type '%s': %s" % (type(obj), str(obj))) raise TypeError("Could not JSON-encode type '%s': %s" % (type(obj), str(obj)))
return out return out
##### #####
# Routes # Routes
@ -343,11 +348,11 @@ def list(count=100, format="HTML"):
out += "\t</bookmark>\n" out += "\t</bookmark>\n"
c += 1 c += 1
out += "</xml>\n" out += "</xml>\n"
return Response(out, mimetype='application/xml') return Response(out, mimetype='application/xml')
elif format == "json": elif format == "json":
blist = Bookmark.objects(deleted=False).order_by("-created_at").only("url","title","short","note","created_at","tags","unread").limit(count) blist = Bookmark.objects(deleted=False).order_by("-created_at").only("url","title","short","note","created_at","tags","unread").limit(count)
out = "" out = ""
@ -387,7 +392,7 @@ def deleted(count=100, format="HTML"):
return blist.to_json() return blist.to_json()
else: else:
return render_template("list.html", blist=blist, loc=loc) return render_template("list.html", blist=blist, loc=loc)
# List unread bookmarks # List unread bookmarks
@app.route('/unread/<int:count>/<format>') @app.route('/unread/<int:count>/<format>')
@app.route('/unread/<int:count>/') @app.route('/unread/<int:count>/')
@ -401,31 +406,31 @@ def unread(count=100, format="HTML"):
return blist.to_json() return blist.to_json()
else: else:
return render_template('list.html', blist=blist, loc=loc) return render_template('list.html', blist=blist, loc=loc)
# New bookmark # New bookmark
@app.route('/new', methods=["GET", "POST"]) @app.route('/new', methods=["GET", "POST"])
@login_required @login_required
def new(): def new():
if request.method=="POST": if request.method=="POST":
b = Bookmark() b = Bookmark()
b.title = request.form["title"] b.title = request.form["title"]
b.short = str(b.get_short()) b.short = str(b.get_short())
b.note = request.form["note"] b.note = request.form["note"]
try: try:
if request.form["image_embed"]: if request.form["image_embed"]:
b.image_embed = True b.image_embed = True
except: except:
b.image_embed = False b.image_embed = False
try: try:
if request.form["unread"]: if request.form["unread"]:
b.unread = True b.unread = True
except: except:
b.unread = False b.unread = False
try: try:
if request.form["archive"]: if request.form["archive"]:
b.archive_image_needed = True b.archive_image_needed = True
@ -439,8 +444,8 @@ def new():
t = Tag.objects.get_or_create(name=rawtag)[0].save() t = Tag.objects.get_or_create(name=rawtag)[0].save()
tag_list.append(t) tag_list.append(t)
b.tags = tag_list b.tags = tag_list
if request.form["url"] == "": if request.form["url"] == "":
file = request.files['file_upload'] file = request.files['file_upload']
ext = file.filename.rsplit('.',1)[1] ext = file.filename.rsplit('.',1)[1]
filename = b.short + "." + ext filename = b.short + "." + ext
@ -449,15 +454,15 @@ def new():
b.factor = b.get_factor() b.factor = b.get_factor()
b.save() b.save()
return render_template("detail.html", b=b) return render_template("detail.html", b=b)
elif request.form["url"] != "": elif request.form["url"] != "":
b.url = request.form["url"] b.url = request.form["url"]
b.factor = b.get_factor() b.factor = b.get_factor()
b.save() b.save()
return render_template("detail.html", b=b) return render_template("detail.html", b=b)
return render_template("form.html", action="/new") return render_template("form.html", action="/new")
else: else:
b = False b = False
if any(k in request.args.keys() for k in ('title','url','note')): if any(k in request.args.keys() for k in ('title','url','note')):
@ -473,9 +478,9 @@ def new():
b.url = "" b.url = ""
if 'note' in request.args.keys(): if 'note' in request.args.keys():
b.note = request.args['note'] b.note = request.args['note']
else: else:
b.note = "" b.note = ""
return render_template("form.html", action="/new", b=b) return render_template("form.html", action="/new", b=b)
@ -486,7 +491,7 @@ def tagsearch(rawtag):
blist = Bookmark.objects(tags__in=[t]) blist = Bookmark.objects(tags__in=[t])
if blist.count() > 0 : if blist.count() > 0 :
return render_template('list.html',blist=blist) return render_template('list.html',blist=blist)
else: else:
return redirect("/", code=302) return redirect("/", code=302)
@app.route('/<id>/update/<action>') @app.route('/<id>/update/<action>')
@ -500,7 +505,7 @@ def update(id,action):
if 'anchor' in request.args.keys(): if 'anchor' in request.args.keys():
app.logger.debug(request.args['anchor']) app.logger.debug(request.args['anchor'])
loc = loc + "#" + request.args['anchor'] loc = loc + "#" + request.args['anchor']
b = Bookmark.objects(short=id).first() b = Bookmark.objects(short=id).first()
if action == "text": if action == "text":
update_archived_text(b) update_archived_text(b)
@ -538,28 +543,28 @@ def details(id):
@app.route('/<id>/edit', methods=["GET", "POST"]) @app.route('/<id>/edit', methods=["GET", "POST"])
@app.route('/<id>/e', methods=["GET", "POST"]) @app.route('/<id>/e', methods=["GET", "POST"])
@login_required @login_required
def edit(id): def edit(id):
b = Bookmark.objects(short=id).first() b = Bookmark.objects(short=id).first()
if request.method=="POST": if request.method=="POST":
if "title" in request.form.keys(): if "title" in request.form.keys():
b.title = request.form["title"] b.title = request.form["title"]
if "note" in request.form.keys(): if "note" in request.form.keys():
b.note = request.form["note"] b.note = request.form["note"]
if "image_embed" in request.form.keys() and \ if "image_embed" in request.form.keys() and \
request.form['image_embed'] == "checked": request.form['image_embed'] == "checked":
b.image_embed = True b.image_embed = True
else: else:
b.image_embed = False b.image_embed = False
if "unread" in request.form.keys() and \ if "unread" in request.form.keys() and \
request.form['unread'] == "checked": request.form['unread'] == "checked":
b.unread = True b.unread = True
else: else:
b.unread = False b.unread = False
if "archive_text_needed" in request.form.keys() and \ if "archive_text_needed" in request.form.keys() and \
request.form['archive_text_needed'] == "checked": request.form['archive_text_needed'] == "checked":
b.archive_text_needed = True b.archive_text_needed = True
@ -577,19 +582,19 @@ def edit(id):
t = Tag.objects.get_or_create(name=rawtag)[0].save() t = Tag.objects.get_or_create(name=rawtag)[0].save()
tag_list.append(t) tag_list.append(t)
b.tags = tag_list b.tags = tag_list
if "url" in request.form.keys(): if "url" in request.form.keys():
b.url = request.form["url"] b.url = request.form["url"]
b.factor = b.get_factor() b.factor = b.get_factor()
b.save() b.save()
if b: if b:
return render_template("form.html", action = "/"+b.short+"/edit", b=b) return render_template("form.html", action = "/"+b.short+"/edit", b=b)
else: else:
return redirect("/", code=302) return redirect("/", code=302)
# Pull up an archived and parsed text view of the Bookmark # Pull up an archived and parsed text view of the Bookmark
# The first line of defense in preventing link rot... # The first line of defense in preventing link rot...
@ -664,7 +669,7 @@ def embed(id):
else: else:
return redirect("/", code=302) return redirect("/", code=302)
# Short code redirects directly to bookmark target, does not require auth to use # Short code redirects directly to bookmark target, does not require auth to use
# bookie as a URL shortener app # bookie as a URL shortener app
@app.route('/<id>') @app.route('/<id>')
def short(id): def short(id):
@ -676,11 +681,11 @@ def short(id):
return redirect("/"+b.short+"/embed", code=302) return redirect("/"+b.short+"/embed", code=302)
else: else:
return redirect(b.url, code=302) return redirect(b.url, code=302)
else: else:
return redirect("/", code=302) return redirect("/", code=302)
# Anonymous home page # Anonymous home page
@app.route('/') @app.route('/')
def index(): def index():
return render_template("index.html") return render_template("index.html")