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