def fetchtl(): """ Return the requested timeline and note the fetch. """ try: tlid = dbacc.reqarg("tlid", "dbid") if tlid: tl = dbacc.cfbk("Timeline", "dsId", str(tlid), required=True) else: slug = dbacc.reqarg("slug", "string") if not slug: slug = "default" slug = slug.lower() # in case someone camelcased the url. tl = dbacc.cfbk("Timeline", "slug", slug, required=True) tls = contained_timelines(tl) # Note the timeline was fetched for daily stats tracking det = { "referer": flask.request.headers.get("Referer", ""), "useragent": flask.request.headers.get("User-Agent", ""), "tlid": tl["dsId"], "tlname": tl["name"], "uid": dbacc.reqarg("uid", "dbid") } dcd = { "dsType": "DayCount", "tstamp": dbacc.timestamp(), "rtype": "tlfetch", "detail": json.dumps(det) } dbacc.write_entity(dcd) except ValueError as e: return util.serve_value_error(e) return util.respJSON(tls)
def authenticate(): emaddr = dbacc.reqarg("an", "AppUser.email") if not emaddr: emaddr = dbacc.reqarg("email", "AppUser.email") if not emaddr: raise ValueError("'an' or 'email' parameter required") emaddr = normalize_email(emaddr) appuser = dbacc.cfbk("AppUser", "email", emaddr) if not appuser: raise ValueError(emaddr + " not found") dbacc.entcache.cache_put(appuser) # will likely reference again soon reqtok = dbacc.reqarg("at", "string") if not reqtok: password = dbacc.reqarg("password", "string") if not password: raise ValueError("Access token or password required") phash = make_password_hash(emaddr, password, appuser["created"]) if phash == appuser["phash"]: # authenticated, rebuild token reqtok = token_for_user(appuser) else: # failing now due to lack of token, log for analysis if needed logging.info(appuser["email"] + " password hash did not match") logging.info(" AppUser[\"phash\"]: " + appuser["phash"]) logging.info(" Server phash: " + phash) srvtok = token_for_user(appuser) if reqtok != srvtok: logging.info(appuser["email"] + " authenticated token did not match") logging.info(" reqtok: " + reqtok) logging.info(" srvtok: " + srvtok) raise ValueError("Wrong password") return appuser, srvtok
def upldpic(): """ Form submit and monitoring for uploading a point pic. """ # flask.request.method always returns "GET", so check for file input. picfile = flask.request.files.get("picfilein") if not picfile: logging.info("upldpic ready for upload") return util.respond("Ready", mimetype="text/plain") try: appuser, _ = util.authenticate() ptid = dbacc.reqarg("ptid", "dbid", required=True) pt = dbacc.cfbk("Point", "dsId", ptid, required=True) logging.info(appuser["email"] + " upldpic Point " + str(ptid)) if not appuser["dsId"] in util.csv_to_list(pt["editors"]): raise ValueError("Not authorized to edit this point") img = Image.open(picfile) img = ImageOps.exif_transpose(img) # correct vertical orientation sizemaxdims = 400, 400 # max allowed width/height for thumbnail resize img.thumbnail(sizemaxdims) # modify, preserving aspect ratio bbuf = io.BytesIO() # file-like object for save img.save(bbuf, format="PNG") pt["pic"] = base64.b64encode(bbuf.getvalue()) pt = dbacc.write_entity(pt, pt["modified"]) except ValueError as e: logging.info("upldpic Point " + str(ptid) + ": " + str(e)) return util.serve_value_error(e) return util.respond("Done: " + pt["modified"], mimetype="text/plain")
def update_prebuilt(tldat, tldb): lpx = "update_prebuilt Timeline " + str(tldat.get("dsId", "new") + " ") # make a reference dict out of whatever existing preb is available preb = tldat.get("preb") if not preb and tldb: preb = tldb.get("preb") preb = preb or "[]" preb = util.load_json_or_default(preb, []) ptd = {k["dsId"]: k for k in preb} # update any ptd entries that were modified since last timeline save if tldb and tldat.get("cids"): logging.info(lpx + "fetching points updated since last timeline save") where = ("WHERE modified > \"" + tldb["modified"] + "\"" + "AND dsId IN (" + tldat["cids"] + ")") points = dbacc.query_entity("Point", where) for point in points: ptd[point["dsId"]] = point_preb_summary(point) # rebuild preb, fetching points for any missing ptd entries logging.info(lpx + "rebuilding preb") preb = [] for pid in util.csv_to_list(tldat.get("cids", "")): summary = ptd.get(pid) # dict or None if not summary: point = dbacc.cfbk("Point", "dsId", pid) if point: summary = point_preb_summary(point) if not summary: logging.info(lpx + "No point info for pid " + pid) else: # have summary info preb.append(summary) tldat["preb"] = json.dumps(preb)
def verify_new_email_valid(emaddr): # something @ something . something if not re.match(r"[^@]+@[^@]+\.[^@]+", emaddr): raise ValueError("Invalid email address: " + emaddr) existing = dbacc.cfbk("AppUser", "email", emaddr) if existing: raise ValueError("Email address already used") return emaddr
def set_point_srctls(tlid, ptidcsv): if ptidcsv: ptids = ptidcsv.split(",") for ptid in ptids: point = dbacc.cfbk("Point", "dsId", ptid) if point: point["srctl"] = tlid dbacc.write_entity(point, point["modified"])
def verify_db_instance(fbase, fields, fob): dbob = dbacc.cfbk(fob["dsType"], "importid", fob["importid"]) if not dbob: for fieldname, fattrs in fields.items(): if fattrs["pt"] == "image" and fob[fieldname]: imgfilename = fbase + "images/" + fob["importid"] + ".png" with open(imgfilename, 'rb') as imagefile: bdat = imagefile.read() fob[fieldname] = base64.b64encode(bdat) logging.info("Writing " + fob["dsType"] + fob["importid"]) dbacc.write_entity(fob)
def supphelp(): accurl = "Nope." try: administrator_auth() emaddr = dbacc.reqarg("email", "AppUser.email", required=True) appuser = dbacc.cfbk("AppUser", "email", emaddr) accurl = (site_home() + "/timeline/default?an=" + urllib.parse.quote(appuser["email"]) + "&at=" + token_for_user(appuser)) except ValueError as e: return serve_value_error(e) return accurl
def convert_dbid(entity, importid): if not importid: return "" enti2d = imp2ds.get(entity) if not enti2d: imp2ds[entity] = {} enti2d = imp2ds[entity] dsId = enti2d.get(importid) if not dsId: refobj = dbacc.cfbk(entity, "importid", importid, required=True) enti2d[importid] = refobj["dsId"] dsId = refobj["dsId"] return dsId
def verify_active_user(uid, user=None): if not uid: raise ValueError("Valid uid required, received: '" + str(uid) + "'") users = rst["dets"]["users"] if not users.get(uid): if not user: user = dbacc.cfbk("AppUser", "dsId", uid, required=True) users[uid] = { "name": user["name"], "created": user["created"], "modified": user["modified"], "ptedits": {} }
def obimg(): """ Return the associated image for the give object, or a blank 4x4. """ # The client might make a call to get a pic for an object which might # not have one. Better to return a blank than an error in that case. imgdat = B64ENCTRANSPARENT4X4PNG try: dsType = dbacc.reqarg("dt", "string", required=True) dsId = dbacc.reqarg("di", "string", required=True) inst = dbacc.cfbk(dsType, "dsId", dsId) if inst: picfldmap = {"Point": "pic"} imgdat = inst[picfldmap[dsType]] imgdat = base64.b64decode(imgdat) except ValueError as e: return util.serve_value_error(e) return util.respond(imgdat, mimetype="image/png")
def fetchobj(): """ General purpose object retrieval, public info only. """ oj = "" try: dsType = dbacc.reqarg("dt", "string", required=True) keyfld = dbacc.reqarg("ak", "string") # alternate key if keyfld: fldval = dbacc.reqarg("kv", "string", required=True) else: keyfld = "dsId" fldval = dbacc.reqarg("di", "string", required=True) inst = dbacc.cfbk(dsType, keyfld, fldval) if not inst: raise ValueError(dsType + " " + keyfld + ": " + fldval + " not found") oj = util.safe_JSON(inst) except ValueError as e: return util.serve_value_error(e) return util.respJSON("[" + oj + "]")
def verify_edit_authorization(appuser, obj): if not obj.get("dsType") in ["Point", "Timeline"]: raise ValueError("Unknown object type for edit authorization") if not obj.get("dsId"): obj["editors"] = appuser["dsId"] return None # Done. Anyone may create a new Point or Timeline dbo = dbacc.cfbk(obj["dsType"], "dsId", obj["dsId"], required=True) if not util.val_in_csv(appuser["dsId"], dbo["editors"]): raise ValueError("Not an editor for " + obj["dsType"] + obj["dsId"]) updeds = obj.get("editors") if not updeds: # didn't pass editors info, use existing values obj["editors"] = dbo["editors"] else: # verify editors value is ok if updeds != dbo["editors"]: owner = util.csv_to_list(dbo["editors"])[0] if owner != appuser["dsId"]: if updeds == "deleted": raise ValueError("Only the owner can delete") raise ValueError("Only the owner may change editors") return dbo
def mailpwr(): try: subj = "PastKey.org account password reset link" emaddr = dbacc.reqarg("email", "AppUser.email", required=True) returl = url_for_mail_message() body = "You asked to reset your PastKey account password.\n\n" user = dbacc.cfbk("AppUser", "email", emaddr) if user: logging.info("mailpwr sending access link to " + emaddr) body += "Use this link to access the settings for your account: " body += (returl + "?an=" + urllib.parse.quote(user["email"]) + "&at=" + token_for_user(user) + "\n\n") else: logging.info("mailpwr no account found for " + emaddr) body += "You do not have an account for " + emaddr + ". " body += "Either you have not signed up yet, or you used " body += "a different email address. To create an account " body += "visit " + returl + "\n\n" send_mail(emaddr, subj, body) except ValueError as e: return serve_value_error(e) return respJSON("[]")
def get_connection_service(svcname): cs = dbacc.cfbk("AppService", "name", svcname) if not cs: # create needed placeholder for administrators to update cs = dbacc.write_entity({"dsType": "AppService", "name": svcname}) return cs
def contained_timelines(tl): allts = [tl] if tl["ctype"].startswith("Timelines"): for cid in tl["cids"].split(","): allts.append(dbacc.cfbk("Timeline", "dsId", cid, required=True)) return allts