Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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'])
Ejemplo n.º 4
0
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("")
Ejemplo n.º 5
0
    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()
Ejemplo n.º 6
0
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
Ejemplo n.º 7
0
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!")
Ejemplo n.º 8
0
def update_user_password(user_id, password):
    u_id = ObjectId(user_id)
    User = assets.User(user_id=u_id)
    User.update_password(password)
Ejemplo n.º 9
0
    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":
Ejemplo n.º 10
0
    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
Ejemplo n.º 11
0
    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
Ejemplo n.º 12
0
    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)