コード例 #1
0
ファイル: admin.py プロジェクト: Immortalem/fluxscoreboard
 def massmail(self):
     """
     Send a massmail to all users in the system. It also stores the sent
     mail and its recipients in the database to keep a permanent record of
     sent messages.
     """
     form = MassMailForm(self.request.POST, csrf_context=self.request)
     if not form.from_.data:
         settings = self.request.registry.settings
         form.from_.data = settings["mail.default_sender"]
     mail_query = DBSession.query(MassMail)
     page = self.page(mail_query)
     retparams = {"form": form, "items": page.items, "page": page}
     if self.request.method == "POST":
         if not form.validate():
             return retparams
         teams = get_active_teams()
         # Create a record to keep track of all sent mails
         mail_record = MassMail()
         form.populate_obj(mail_record)
         recipients = [team.email for team in teams]
         mail_record.recipients = recipients
         mailer = get_mailer(self.request)
         message = Message(
             subject=mail_record.subject,
             bcc=mail_record.recipients,
             body=mail_record.message,
             sender=mail_record.from_,
         )
         DBSession.add(mail_record)
         mailer.send(message)
         self.request.session.flash("Mass mail sent to all %d active users" % len(recipients))
         return HTTPFound(location=self.request.route_url("admin_massmail"))
     return retparams
コード例 #2
0
ファイル: admin.py プロジェクト: Immortalem/fluxscoreboard
    def submissions_edit(self):
        # Prepare parameters
        form = SubmissionForm(self.request.POST, csrf_context=self.request)
        submissions = get_submissions()
        page = self.page(submissions)
        redirect = self.redirect("admin_submissions", page.page)

        # Cancel button pressed?
        if not form.submit.data:
            return redirect

        is_new = not bool(form.challenge.data and form.team.data)
        # Form errors?
        if not form.validate():
            return self._list_retparams(page, form, is_new=is_new)

        # New item or existing one?
        try:
            submission = (
                DBSession.query(Submission)
                .filter(Submission.challenge_id == form.challenge.data.id)
                .filter(Submission.team_id == form.team.data.id)
                .one()
            )
            self.request.session.flash("Submission edited!")
        except NoResultFound:
            submission = Submission()
            DBSession.add(submission)
            self.request.session.flash("Submission added!")

        # Transfer edits into databse
        form.populate_obj(submission)
        return redirect
コード例 #3
0
ファイル: conftest.py プロジェクト: Immortalem/fluxscoreboard
def database(request, testapp):
    dbsession = DBSession()
    Base.metadata.create_all(bind=dbsession.connection())
    if not dbsession.query(Settings).all():
        dbsession.add(Settings())

    def _drop():
        conn = dbsession.connection()
        Base.metadata.drop_all(bind=conn)
        # TODO: Why do we have to use this literally?
        # If fixed test against MySQL *and* Postgres!
        conn.execute("COMMIT")
    request.addfinalizer(_drop)
コード例 #4
0
ファイル: team.py プロジェクト: Immortalem/fluxscoreboard
def register_ip(event):
    if ("test-login" in event.request.session and
            event.request.session["test-login"] or
            event.request.path.startswith('/static')):
        return None
    team_id = event.request.authenticated_userid
    t = transaction.savepoint()
    if not team_id:
        return
    ip = unicode(event.request.client_addr)
    try:
        DBSession.add(TeamIP(team_id=team_id, ip=ip))
        DBSession.flush()
    except IntegrityError:
        t.rollback()
コード例 #5
0
ファイル: install.py プロジェクト: Immortalem/fluxscoreboard
def install(settings, cfg_file, test_data=False):
    """
    Installs the application. Only required to be called once per installation.
    """
    dbsession = DBSession()
    transaction.begin()
    try:
        install_alembic_table(cfg_file)
        Base.metadata.create_all(bind=dbsession.connection())
        create_country_list(dbsession)
        if test_data:
            install_test_data(dbsession, settings)
        if not dbsession.query(Settings).all():
            dbsession.add(Settings())
        for dyn_mod in dynamic_challenges.registry.values():
            dyn_mod.install(dbsession.connection())
    except:
        transaction.abort()
        raise
    else:
        transaction.commit()
コード例 #6
0
ファイル: admin.py プロジェクト: Immortalem/fluxscoreboard
    def _admin_edit(self, route_name, FormClass, DatabaseClass, title):
        """
        A generic function for a view that is invoked after an edit (or add)
        has been performed. It is separate from that of
        :meth:`AdminView._admin_list` to keep the code cleaner. It has the
        same parameters and return types but can only be invoked as a ``POST``.
        """
        # We don't accept GET or others here
        assert self.request.method == "POST"

        # Prepare parameters
        form = FormClass(self.request.POST, csrf_context=self.request)
        page = self.page(self.items(DatabaseClass))
        redirect = self.redirect(route_name, page.page)

        # Cancel button pressed?
        if not form.submit.data:
            return redirect

        # Form errors?
        if not form.validate():
            return self._list_retparams(page, form)

        # New item or existing one?
        if not form.id.data:
            db_item = DatabaseClass()
            DBSession.add(db_item)
            self.request.session.flash("%s added!" % title)
        else:
            db_item = DBSession.query(DatabaseClass).filter(DatabaseClass.id == form.id.data).one()
            self.request.session.flash("%s edited!" % title)

        # Transfer edits into database
        form.populate_obj(db_item)
        if db_item.id == "":
            # Safe measure to ensure a clean item ID
            db_item.id = None
        return redirect
コード例 #7
0
ファイル: team.py プロジェクト: Immortalem/fluxscoreboard
def register_team(form, request):
    """
    Create a new team from a form and send a confirmation email.

    Args:
        ``form``: A filled out
        :class:`fluxscoreboard.forms.front.RegisterForm`.

        ``request``: The corresponding request.

    Returns:
        The :class:`Team` that was created.
    """
    team = Team(name=form.name.data,
                email=form.email.data,
                password=form.password.data,
                country=form.country.data,
                timezone=form.timezone.data,
                size=form.size.data,
                )
    DBSession.add(team)
    DBSession.flush()
    send_activation_mail(team, request)
    return team
コード例 #8
0
def check_submission(challenge, solution, team, settings):
    """
    Check a solution for a challenge submitted by a team and add it to the
    database if it was correct.

    Args:
        ``challenge``: An instance of :class:`Challenge`, the challenge to
        check the solution for.

        ``solution``: A string, the proposed solution for the challenge.

        ``team``: Team that submitted the solution.

    Returns:
        A tuple of ``(result, msg)``. ``result`` indicates whether the solution
        was accpeted (and added to the database) or not. The message returns
        a string with either a result (if ``result == False``) or a
        congratulations message.
    """
    # Perform all checks that filter out invalid submissions
    if settings.submission_disabled:
        return False, "Submission is currently disabled"

    if not settings.archive_mode and now() > settings.ctf_end_date:
        return False, "The CTF is over, no more solutions can be submitted."

    if not challenge.online:
        return False, "Challenge is offline."

    if challenge.manual:
        return False, "Credits for this challenge will be given manually."

    if challenge.dynamic:
        return False, "The challenge is dynamic, no submission possible."

    # help faggots
    solution = solution.strip()
    if solution.startswith('flag{'):
        solution = solution[5:-1]

    if challenge.solution != solution:
        return False, "Solution incorrect."

    # After this, the solution is correct and we can return True
    if settings.archive_mode:
        return True, ("Congratulations: That was the correct solution! "
                      "However, since the scoreboard is in archive mode, you "
                      "will not be awarded any points.")

    query = (DBSession.query(Submission.team_id).
             filter(Submission.challenge_id == challenge.id))
    submissions = [id_ for id_, in query]

    if team.id in submissions:
        return False, "Already solved."

    solved_count = len(submissions)
    first_blood_pts, place_msg = first_blood_map.get(solved_count, (0, None))
    if place_msg is not None:
        msg = 'Congratulations: You solved this challenge as %s!' % place_msg
    else:
        msg = 'Congratulations: That was the correct solution!'

    msg += (' How did you like this challenge? Please provide some feedback '
            'in the form below.')

    submission = Submission(additional_pts=first_blood_pts)
    submission.team_id = team.id
    submission.challenge = challenge
    DBSession.add(submission)
    team.base_score += challenge.base_points + first_blood_pts
    team.bonus_score += challenge.points - challenge.base_points
    return True, msg
コード例 #9
0
ファイル: front.py プロジェクト: Immortalem/fluxscoreboard
 def challenge(self):
     """
     A view of a single challenge. The query is very similar to that of
     :meth:`challenges` with the limitation that only one challenge is
     fetched. Additionally, this page displays a form to enter the solution
     of that challenge and fetches a list of announcements for the
     challenge.
     """
     challenge_id = int(self.request.matchdict["id"])
     team_id = self.request.authenticated_userid
     team_solved_subquery = get_team_solved_subquery(team_id)
     try:
         challenge, is_solved = (
             DBSession.query(Challenge, team_solved_subquery).
             filter(Challenge.id == challenge_id).
             filter(Challenge.published).one()
         )
     except NoResultFound:
         self.request.session.flash("Challenge not found or published.")
         return HTTPFound(location=self.request.route_url('challenges'))
     form = SolutionSubmitForm(self.request.POST, csrf_context=self.request)
     retparams = {'challenge': challenge,
                  'form': form,
                  'is_solved': is_solved,
                  }
     # solved or after CTF
     feedback_obj = None
     if is_solved or (CTF_ENDED, True) == self.current_state:
         feedback_obj = (
             DBSession.query(Feedback).
             filter(Feedback.team_id == self.request.team.id).
             filter(Feedback.challenge_id == challenge.id).
             first())
         if not feedback_obj:
             feedback_obj = Feedback(team_id=self.request.team.id,
                                     challenge_id=challenge.id)
             DBSession.add(feedback_obj)
         retparams['feedback'] = FeedbackForm(
             self.request.POST, obj=feedback_obj,
             csrf_context=self.request)
     if self.request.method == 'POST':
         if 'submit_feedback' in self.request.POST:
             if not retparams['feedback'].validate():
                 return retparams
             if feedback_obj:
                 retparams['feedback'].populate_obj(feedback_obj)
                 self.request.session.flash(
                     'Thanks for your feedback. You can edit it at any '
                     'point.')
         else:
             if not form.validate():
                 return retparams
             is_solved, msg = check_submission(
                 challenge, form.solution.data, self.request.team,
                 self.request.settings)
             self.request.session.flash(msg,
                                        'success' if is_solved else 'error')
         return HTTPFound(location=self.request.route_url('challenge',
                                                          id=challenge.id)
                          )
     return retparams