def prune_sessions(): """ Removes sessions older than 24 hours. """ yesterday = datetime.now() - timedelta(days=1) logger.debug("Searching for sessions older than %s to prune..." % yesterday) old_sessions = mdb.sessions.find({ "created_on": { "$lt": yesterday } }).sort("created_on") logger.debug("%s sessions found." % old_sessions.count()) pruned_sessions = 0 preserved_sessions = 0 for s in old_sessions: user = mdb.users.find_one({"login": s["login"]}) U = assets.User(user["_id"], session_object=admin_session) if U.is_admin(): # logger.debug("Preserving session for admin user '%s'" % user["login"]) preserved_sessions += 1 elif U.get_preference("preserve_sessions") and s[ "created_on"] > thirty_days_ago: # if it's older than 30 days, ignore the preference # logger.debug("Preserving session for user '%s' because of user preference." % user["login"]) preserved_sessions += 1 else: s_id = s["_id"] logger.debug("Pruning old session '%s' (%s)" % (s_id, s["login"])) remove_session(s_id, "admin") pruned_sessions += 1 if pruned_sessions > 0: logger.info("Pruned %s old sessions." % pruned_sessions) if preserved_sessions > 0: logger.info("Preserved %s sessions due to user preference." % preserved_sessions)
def export_data(u_id): """ Writes a pickle of the user and his assets. """ U = assets.User(user_id=u_id, session_object=admin_session) filename = "%s.%s.admin_export.pickle" % (datetime.now().strftime(ymd), U.user["login"]) fh = file(filename, "w") fh.write(U.dump_assets("pickle")) fh.close() print("Export successful!\n -> %s" % filename)
def update_user(u_id, remove_attrib=None): User = assets.User(user_id=ObjectId(u_id), session_object=admin_session) try: del User.user[remove_attrib] User.save() print("\n Removed '%s' attribute from %s\n" % (remove_attrib, User)) except Exception as e: print("\n Could not remove attribute '%s' from %s!\n" % (remove_attrib, User)) dump_document('users', User.user['_id'])
def pretty_view_user(u_id): """ Prints a pretty summary of the user and his assets to STDOUT. """ User = assets.User(u_id, session_object=admin_session) if User.user is None: print("Could not retrieve user info from mdb.") return None print("\n\tUser Summary!\n\n _id: %s\n login: %s\n created: %s\n" % (User.user["_id"], User.user["login"], User.user["created_on"])) for u_key in sorted(User.user.keys()): if u_key not in ["_id", "login", "created_on", "activity_log"]: print(" %s: %s" % (u_key, User.user[u_key])) print("") def asset_repr(a, type=False): if type == "settlement": return " %s - %s - LY: %s (%s/%s)" % ( a["_id"], a["name"], a["lantern_year"], a["population"], a["death_count"]) if type == "survivor": return " %s - %s [%s] %s" % (a["_id"], a["name"], a["sex"], a["email"]) if User.settlements == []: print(" No settlements.\n") else: print("\t%s settlements:\n" % len(User.settlements)) for settlement in User.settlements: print asset_repr(settlement, "settlement") settlement_survivors = mdb.survivors.find( {"settlement": settlement["_id"]}) if settlement_survivors.count() > 0: for survivor in settlement_survivors: print(" %s" % asset_repr(survivor, "survivor")) else: print(" -> No survivors in mdb.") print("") if User.get_survivors() is None: print(" No survivors.\n") else: print("\t%s survivors:\n" % len(User.get_survivors())) for survivor in User.get_survivors(): print asset_repr(survivor, "survivor") print("") if "activity_log" in User.user.keys(): print(" Last 10 Actions:") for entry in User.user["activity_log"]: dt, action = entry print(" %s | %s" % (dt.strftime(ymdhms), action)) print("")
def new(self, login, password): """ Creates a new session. Needs a valid user login and password.. Updates the session with a User object ('self.User') and a new Session object ('self.session'). """ user = mdb.users.find_one({"login": login}) mdb.sessions.remove({"login": user["login"]}) # new! get a JWT token and add it to your sesh so that your sesh can be # used to add it to your cookie token = api.get_jwt_token(login, password) if token: self.logger.debug("[%s (%s)] JWT token retrieved!" % (user["login"], user["_id"])) session_dict = { "login": login, "created_on": datetime.now(), "created_by": user["_id"], "current_view": "dashboard", "user_agent": { "is_mobile": get_user_agent().is_mobile, "browser": get_user_agent().browser }, "access_token": token, } session_id = mdb.sessions.insert(session_dict) self.session = mdb.sessions.find_one({"_id": session_id}) # update the user with the session ID user["current_session"] = session_id mdb.users.save(user) self.User = assets.User(user["_id"], session_object=self) return session_id # passes this back to the html.create_cookie_js()
def ls_documents(collection, sort_on="created_on"): """ Dumps a list- or ls-style rendering of a collection's contents. """ if collection == "users": sort_on = "latest_activity" if collection == "response_times": print("\n\t View\t\t Avg. response time\t Max response time\n") for a in get_response_times(): print "\t%s\t\t%s\t\t%s" % (a["_id"], a["avg_time"], a["max_time"]) print("") return True for d in mdb[collection].find().sort(sort_on): output = " " if sort_on in d.keys(): output += d[sort_on].strftime("%Y-%m-%d %H:%M:%S") output += " " output += str(d["_id"]) output += " - " if collection == "users" or collection == "sessions": output += d["login"] elif collection == "settlement_events": creator = assets.User(user_id=d["created_by"], session_object={"_id": 0}) output += creator.user["login"] else: output += d["name"] try: output += " <%s> " % mdb.users.find_one( {"_id": d["created_by"]})["login"] except TypeError: output += " <USER NOT FOUND> " try: output += " (%s)" % d["created_on"] except Exception as e: print d raise print output
def import_data(data_pickle_path, force=False): """ Takes a pickle of User and asset data and imports it into the local MDB. This will absolutely overwrite/clobber any documents whose _id values match those in the pickled data. YHBW. """ try: data = pickle.loads(data_pickle_path) except: if not os.path.isfile(data_pickle_path): raise Exception("Path '%s' looks bogus!" % data_pickle_path) else: data = pickle.load(file(data_pickle_path, "rb")) print("\n Importing user %s (%s)" % (data["user"]["login"], data["user"]["_id"])) if "current_session" in data["user"].keys(): del data["user"]["current_session"] try: mdb.users.save(data["user"]) except pymongo.errors.DuplicateKeyError: print(" User login '%s' exists under a different user ID!\n Cannot import user data in %s.\n Exiting...\n" % (data["user"]["login"], data_pickle_path)) sys.exit(255) def import_assets(asset_name): """ Helper function to import assets generically. The 'asset_name' arg should be something like "settlement_events" or "survivors" or "settlements", i.e. one of the keys in the incoming 'assets_dict' dictionary (called 'data' here). """ imported_assets = 0 print(" Importing %s assets..." % asset_name) for asset in data[asset_name]: imported_assets += 1 mdb[asset_name].save(asset) print(" %s %s assets imported." % (imported_assets, asset_name)) import_assets("settlements") import_assets("settlement_events") import_assets("survivors") # sanity check survivor imports here for s in mdb.survivors.find(): if mdb.settlements.find_one({"_id": s["settlement"]}) is None: print(" Survivor %s belongs to a non-existent settlement (%s).\n Removing survivor %s from mdb..." % (s["_id"], s["settlement"], s["_id"])) mdb.survivors.remove({"_id": s["_id"]}) # import avatars here imported_avatars = 0 for avatar in data["avatars"]: if gridfs.GridFS(mdb).exists(avatar["_id"]): gridfs.GridFS(mdb).delete(avatar["_id"]) print(" Removed object %s from local GridFS." % avatar["_id"]) gridfs.GridFS(mdb).put(avatar["blob"], _id=avatar["_id"], content_type=avatar["content_type"], created_by=avatar["created_by"], created_on=avatar["created_on"]) imported_avatars += 1 print(" Imported %s avatars!" % imported_avatars) mdb.sessions.remove({"login": data["user"]["login"]}) print(" Removed session(s) belonging to incoming user.") if force: manual_approve = "YES" else: manual_approve = raw_input(' Reset password for %s? Type "YES" to reset: ' % data["user"]["login"]) if manual_approve == "YES": U = assets.User(user_id=data["user"]["_id"], session_object=admin_session) U.update_password("password") print(" Updated password to 'password'. ") else: print(" Password has NOT been reset.") print(" Import complete!")
def update_user_password(user_id, password): u_id = ObjectId(user_id) User = assets.User(user_id=u_id) User.update_password(password)
if options.toggle_admin: print("\n This is no longer supported via the legacy webapp admin tools! Please use API controls.\n") sys.exit(1) if options.tail_settlement: tail(options.tail_settlement) if options.export_data: export_data(options.export_data) if options.import_data: import_data(options.import_data) if options.user_repr: User = assets.User(user_id=options.user_repr, session_object=admin_session) print User.dump_assets() if options.user_id and options.user_pass: update_user_password(options.user_id, options.user_pass) if options.user_id and options.remove_attrib: update_user(options.user_id, remove_attrib=options.remove_attrib) if options.initialize: print(" hostname: %s" % socket.gethostname()) if socket.gethostname() not in ["paula.local", "mona"]: print("This isn't the dev machine!") sys.exit(1) manual_approve = raw_input('Initialize the project and remove all data? Type "YES" to proceed: ') if manual_approve == "YES":
def render_html(self): """ Renders the whole panel. """ try: World = api.route_to_dict("world") W = World["world"] meta = World["meta"] except Exception as e: return "World could not be loaded! %s" % e daemon_block = '<table class="admin_panel_right_child">' daemon_block += '<tr><th colspan="2">World Daemon</th></tr>' for k, v in World["world_daemon"].iteritems(): if type(v) == dict: raw = v["$date"] dt = datetime.fromtimestamp(raw/1000) daemon_block += '<tr><td>%s</td><td>%s</td></tr>' % (k,dt) else: daemon_block += '<tr><td>%s</td><td>%s</td></tr>' % (k,v) daemon_block += "</table>" response_block = '<table class="admin_panel_right_child">' response_block += '<tr><th colspan="4">Response Times</th></tr>' response_block += '<tr><td><i>View</i></td><td><i>#</i></td><td><i>Avg.</i></td><td><i>Max</i></td></tr>' for r in get_response_times(): response_block += "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>" % (r["_id"],r["count"],r["avg_time"],r["max_time"]) response_block += "</table>" admins_block = '<table class="admin_panel_right_child">' admins_block += '<tr><th colspan="2">Administrators</th></tr>' admins = mdb.users.find({"admin":{"$exists":True}}) for a in admins: admins_block += '<tr><td>%s</td><td>%s</td></tr>' % (a["login"],a["_id"]) admins_block += '</table>' stat_block = '<table class="admin_panel_right_child">' stat_block += '<tr><th colspan="2">Environment</th></tr>' stat_block += '<tr><td>Host</td><td>%s</td></tr>' % socket.getfqdn() stat_block += '<tr><td>Application version</td><td>%s</td></tr>' % settings.get("application","version") stat_block += '<tr><td>Release</td><td>%s</td></tr>' % get_latest_update_string() stat_block += '<tr><td>API URL</td><td>%s</td></tr>' % api.get_api_url() stat_block += '<tr><td>API admin panel</td><td><a href="%sadmin">%sadmin</a></td></tr>' % (api.get_api_url(), api.get_api_url()) stat_block += '<tr><td>API Version</td><td>%s</td></tr>' % meta["api"]["version"] stat_block += '</table>' output = html.panel.headline.safe_substitute( response_times = response_block, world_daemon = daemon_block, recent_users_count = self.recent_users.count(), users = W["total_users"]["value"], sessions = mdb.sessions.find().count(), settlements = mdb.settlements.find().count(), total_survivors = W["total_survivors"]["value"], live_survivors = W["live_survivors"]["value"], dead_survivors = W["dead_survivors"]["value"], complete_death_records = mdb.the_dead.find({"complete": {"$exists": True}}).count(), latest_fatality = world.api_survivor_to_html(W["latest_fatality"]), latest_settlement = world.api_settlement_to_html(W["latest_settlement"]), latest_kill = world.api_monster_to_html(W["latest_kill"]), current_hunt = world.api_current_hunt(W["current_hunt"]), admin_stats = stat_block, admins = admins_block, ) for user in self.recent_users: User = assets.User(user_id=user["_id"], session_object=self.Session) # create settlement summaries settlements = mdb.settlements.find({"created_by": User.user["_id"]}).sort("name") settlement_strings = [] for s in settlements: S = assets.Settlement(settlement_id=s["_id"], session_object=self.Session, update_mins=False) settlement_strings.append(S.render_admin_panel_html()) output += html.panel.user_status_summary.safe_substitute( user_name = User.user["login"], u_id = User.user["_id"], ua = User.user["latest_user_agent"], latest_sign_in = User.user["latest_sign_in"].strftime(ymdhms), latest_sign_in_mins = (datetime.now() - User.user["latest_sign_in"]).seconds // 60, session_length = (User.user["latest_activity"] - User.user["latest_sign_in"]).seconds // 60, latest_activity = User.user["latest_activity"].strftime(ymdhms), latest_activity_mins = (datetime.now() - User.user["latest_activity"]).seconds // 60, latest_action = User.user["latest_action"], settlements = "<br/>".join(settlement_strings), survivor_count = mdb.survivors.find({"created_by": User.user["_id"]}).count(), user_created_on = User.user["created_on"].strftime(ymd), user_created_on_days = (datetime.now() - User.user["created_on"]).days ) output += "<hr/>" log_lines = self.get_last_n_log_lines(50) zebra = False for l in reversed(log_lines): if zebra: output += html.panel.log_line.safe_substitute(line=l, zebra=zebra) zebra = False else: output += html.panel.log_line.safe_substitute(line=l) zebra = "grey" output += html.meta.hide_full_page_loader return output
def __init__(self, params={}): """ Initialize a new Session object.""" self.logger = get_logger() # these are our session attributes. Declare them all here self.params = params self.session = None self.Settlement = None self.User = None self.set_cookie = False # # special session types # # we're not processing params yet, but if we have a log out request, we # do it here, while we're initializing a new session object. if "remove_session" in self.params: user = mdb.users.find_one({ "current_session": ObjectId(self.params["remove_session"].value) }) if user is not None: self.User = assets.User(user_id=user["_id"], session_object={"_id": 0}) self.User.mark_usage("signed out") if 'login' in self.params: admin.remove_session(self.params["remove_session"].value, self.params["login"].value) else: admin.remove_session(self.params["remove_session"].value, "webapp_error") # ok, if this is a recovery request, let's try to do that if 'recovery_code' in self.params: self.logger.info("Password Recovery Code sign-in initiated!") user = mdb.users.find_one( {'recovery_code': self.params["recovery_code"].value}) if user is None: self.logger.info( "Password Recovery Code not found (possibly expired). Aborting attempt." ) else: self.logger.info( "Rendering Password Recovery controls for '%s'" % user["login"]) login.render("reset", user["login"], self.params['recovery_code'].value) # # normal session types # # # initialize! # # 1.) try to set the session ID from the cookie self.cookie = Cookie.SimpleCookie(os.environ.get("HTTP_COOKIE")) if "session" in self.cookie: session_id = ObjectId(self.cookie['session'].value) else: session_id = None # 2.) determine if creds are present creds_present = False if 'login' in self.params and 'password' in self.params: creds_present = True # # do stuff! # # default sign in method; def sign_in(): """ Private DRYness method for quickly logging in with params. """ if 'login' in self.params and 'password' in self.params: self.AuthObject = login.AuthObject(self.params) self.User, self.session = self.AuthObject.authenticate() self.set_cookie = True if session_id is not None: self.session = mdb.sessions.find_one({"_id": session_id}) if self.session is None: sign_in() else: user_object = mdb.users.find_one( {"current_session": session_id}) self.User = assets.User(user_object["_id"], session_object=self) elif self.cookie is not None and 'Session' not in self.cookie.keys( ) and creds_present: sign_in() elif self.cookie is None and creds_present: sign_in() else: sign_in() # self.logger.error("Error attempting to process cookie!") # self.logger.error(self.cookie) if self.session is not None: if not api.check_token(self): # self.logger.debug("JWT Token expired! Attempting to refresh...") r = api.refresh_jwt_token(self) if r.status_code == 401: self.log_out() self.session = None
def process_params(self, user_action=None): """ All cgi.FieldStorage() params passed to this object on init need to be processed. This does ALL OF THEM at once. """ # # dashboard-based, user operation params # if "update_user_preferences" in self.params: self.User.update_preferences(self.params) user_action = "updated user preferences" if "change_password" in self.params: if "password" in self.params and "password_again" in self.params: self.User.update_password(self.params["password"].value, self.params["password_again"].value) user_action = "updated password" else: user_action = "failed to update password" # do error reporting if "error_report" in self.params and "body" in self.params: self.report_error() # # change view operations, incl. with/without an asset # # change to a generic view without an asset if "change_view" in self.params: target_view = self.params["change_view"].value user_action = self.change_current_view(target_view) # change to a view of an asset for p in ["view_campaign", "view_settlement", "view_survivor"]: if p in self.params: user_action = self.change_current_view_to_asset(p) # these are our two asset removal methods: this is as DRY as I think we # can get with this stuff, since both require unique handling if "remove_settlement" in self.params: s_id = ObjectId(self.params["remove_settlement"].value) self.set_current_settlement(s_id) S = assets.Settlement(settlement_id=s_id, session_object=self) S.remove() user_action = "removed settlement %s" % S self.change_current_view("dashboard") if "remove_survivor" in self.params: # we actually have to get the survivor from the MDB to set the # current settlement before we can initialize the survivor and # use its remove() method. s_id = ObjectId(self.params["remove_survivor"].value) s_doc = mdb.survivors.find_one({"_id": s_id}) self.set_current_settlement(s_doc["settlement"]) S = assets.Survivor(survivor_id=s_id, session_object=self) S.remove() user_action = "removed survivor %s from %s" % (S, S.Settlement) self.change_current_view("view_campaign", asset_id=S.survivor["settlement"]) # user and campaign exports if "export_user_data" in self.params: export_type = self.params["export_user_data"].value if "asset_id" in self.params and self.User.is_admin(): user_object = assets.User(user_id=self.params["asset_id"].value, session_object=self) filename = "%s_%s.kdm-manager_export.%s" % (datetime.now().strftime(ymd), user_object.user["login"], export_type.lower()) payload = user_object.dump_assets(dump_type=export_type) else: payload = self.User.dump_assets(dump_type=export_type) filename = "%s_%s.kdm-manager_export.%s" % (datetime.now().strftime(ymd), self.User.user["login"], export_type.lower()) self.User.mark_usage("exported user data (%s)" % export_type) self.logger.debug("[%s] '%s' export complete. Rendering export via HTTP..." % (self.User, export_type)) html.render(str(payload), http_headers="Content-Disposition: attachment; filename=%s\n" % (filename)) if "export_campaign" in self.params: export_type = self.params["export_campaign"].value C = assets.Settlement(settlement_id=ObjectId(self.params["asset_id"].value), session_object=self) payload, length = C.export(export_type) filename = "%s_-_%s.%s" % (datetime.now().strftime(ymd), C.settlement["name"], export_type.lower()) self.User.mark_usage("exported campaign data as %s" % export_type) self.logger.debug("[%s] '%s' export complete. Rendering export via HTTP..." % (self.User, export_type)) html.render(payload, http_headers="Content-type: application/octet-stream;\r\nContent-Disposition: attachment; filename=%s\r\nContent-Title: %s\r\nContent-Length: %i\r\n" % (filename, filename, length)) # # settlement operations - everything below uses user_asset_id # which is to say that all forms that submit these params use # the asset_id=mdb_id convention. user_asset_id = None if "asset_id" in self.params: user_asset_id = ObjectId(self.params["asset_id"].value) # create new user assets if "new" in self.params: # # new survivor creation via API call # if self.params["new"].value == "survivor": self.set_current_settlement(user_asset_id) POST_params = {"settlement": self.Settlement.settlement["_id"]} incoming_form_params = ["name","sex","survivor_avatar","father","mother","email","public"] for p in incoming_form_params: if p in self.params and self.params[p].value not in ["",u""]: if "string:" in self.params[p].value: oid = self.params[p].value.split("string:")[1] POST_params[p] = oid else: POST_params[p] = self.params[p].value else: pass response = api.post_JSON_to_route("/new/survivor", payload=POST_params, Session=self) if response.status_code == 200: s_id = ObjectId(response.json()["sheet"]["_id"]["$oid"]) self.change_current_view("view_survivor", asset_id=s_id) S = assets.Survivor(s_id, session_object=self) user_action = "created survivor %s in %s" % (S, self.Settlement) else: msg = "An API error caused survivor creation to fail! API response was: %s - %s" % (response.status_code, response.reason) self.logger.error("[%s] new survivor creation failed!" % self.User) self.logger.error("[%s] %s" % (self.User, msg)) raise RuntimeError(msg) # # new settlement creation via API call # if self.params["new"].value == "settlement": params = {} params["campaign"] = self.params["campaign"].value # try to get a name or default to None if "name" in self.params: params["name"] = self.params["name"].value else: params["name"] = None # try to get expansions/survivors params or default to [] for p in ["expansions", "survivors", "specials"]: if p not in self.params: params[p] = [] elif p in self.params and isinstance(self.params[p], cgi.MiniFieldStorage): params[p] = [self.params[p].value] elif p in self.params and type(self.params[p]) == list: params[p] = [i.value for i in self.params[p]] else: msg = "Invalid form parameter! '%s' is unknown type: '%s'" % (p, type(self.params[p]).__name__) self.logger.error("[%s] invalid param key '%s' was %s. Params: %s" % (self.User, p, type(self.params[p]), self.params)) raise AttributeError(msg) # hit the route; check the response response = api.post_JSON_to_route("/new/settlement", payload=params, Session=self) if response.status_code == 200: s_id = ObjectId(response.json()["sheet"]["_id"]["$oid"]) self.set_current_settlement(s_id) S = assets.Settlement(s_id, session_object=self) user_action = "created settlement %s" % self.Settlement self.change_current_view("view_campaign", S.settlement["_id"]) S.save() elif response.status_code == 405: self.change_current_view('dashboard') else: msg = "An API error caused settlement creation to fail! API response was: %s - %s" % (response.status_code, response.reason) self.logger.error("[%s] new settlement creation failed!" % self.User) self.logger.error("[%s] %s" % (self.User, msg)) raise RuntimeError(msg) # bulk add if "bulk_add_survivors" in self.params: self.set_current_settlement(user_asset_id) S = assets.Settlement(settlement_id=user_asset_id, session_object=self) male = int(self.params["male_survivors"].value) female = int(self.params["female_survivors"].value) for tup in [("M", male), ("F",female)]: letter, sex = tup for i in range(sex): POST_params = {"settlement": self.Settlement.settlement["_id"], "sex": letter, "public": True,} response = api.post_JSON_to_route("/new/survivor", payload=POST_params, Session=self) if response.status_code == 200: pass else: msg = "An API error caused survivor creation to fail! API response was: %s - %s" % (response.status_code, response.reason) self.logger.error("[%s] new survivor creation failed!" % self.User) self.logger.error("[%s] %s" % (self.User, msg)) raise RuntimeError(msg) user_action = "added %s male and %s survivors to %s" % (male, female, self.Settlement) self.change_current_view("view_campaign", user_asset_id) # modify if "modify" in self.params: if self.params["modify"].value == "settlement": s = mdb.settlements.find_one({"_id": ObjectId(user_asset_id)}) self.set_current_settlement(s["_id"]) S = assets.Settlement(settlement_id=s["_id"], session_object=self) S.modify(self.params) user_action = "modified settlement %s" % self.Settlement if self.params["modify"].value == "survivor": update_mins = True if "norefresh" in self.params: update_mins = False s = mdb.survivors.find_one({"_id": ObjectId(user_asset_id)}) self.set_current_settlement(s["settlement"], update_mins) S = assets.Survivor(survivor_id=s["_id"], session_object=self) S.modify(self.params) user_action = "modified survivor %s of %s" % (S, self.Settlement) self.User.mark_usage(user_action)