#60 Add support for requesting an 'I Voted' badge.
Closed 3 years ago by bcotton. Opened 3 years ago by bcotton.
bcotton/elections add_voted_badge  into  master

file added
+1
@@ -0,0 +1,1 @@ 

+ fedoraInfraTox { }

file modified
+1
@@ -7,3 +7,4 @@ 

  .coverage

  alembic.ini

  .vagrant/

+ .tox/

file modified
+31 -4
@@ -43,7 +43,7 @@ 

  [vagrant@localhost ~]$ pushd /vagrant/; ./runserver.py --host "0.0.0.0";

  ```

  

- Once that is running, go to [localhost:5002](http://localhost:5002/) in your

+ Once that is running, go to [localhost:5005](http://localhost:5005/) in your

  browser to see your running Fedora Elections test instance.

  

  ### A note about fonts
@@ -116,6 +116,32 @@ 

  python createdb.py

  ```

  

+ ### Register the application using openid-connect

+ 

+ Run:

+ 

+ ```

+ oidc-register https://iddev.fedorainfracloud.org/ http://localhost:5005/oidc_callback

+ ```

+ 

+ Copy the corresponding ``client_secrets.json`` in the sources:

+ 

+ ```

+ cp client_secrets.json fedora_elections/client_secrets.json

+ ```

+ 

+ ### Create a local configuration file

+ 

+ Run:

+ 

+ ```

+ cat > config <<EOL

+ OIDC_ID_TOKEN_COOKIE_SECURE = False

+ OIDC_REQUIRE_VERIFIED_EMAIL = False

+ EOL

+ ```

+ 

+ 

  ### Starting the app

  

  There are 2 ways to start the application:
@@ -127,10 +153,10 @@ 

  

  This is useful for a quick development instance, when you don't have to worry

  about security yet. Do not run this in production. The server will start on

- http://127.0.0.1:5000.

+ http://127.0.0.1:5005.

  

  ```

- ./runserver

+ ./runserver.py -c config

  ```

  

  #### With Apache
@@ -151,7 +177,8 @@ 

  adjust the `.wsgi` file in `/var/www` to point to the `fedora_elections`

  directory.

  

- Place the configuration file in `/etc/fedora-elections/fedora-elections.cfg`.

+ Place the configuration file in `/etc/fedora-elections/fedora-elections.cfg`

+ and adjust it as you wish.

  

  ```

  sudo mkdir -p /etc/fedora-elections/

@@ -0,0 +1,27 @@ 

+ """Add badge support to elections

+ 

+ Revision ID: 5ecdd55b4af4

+ Revises: 2b8f5a6f10a4

+ Create Date: 2019-03-18 12:21:59.536380

+ 

+ """

+ 

+ # revision identifiers, used by Alembic.

+ revision = '5ecdd55b4af4'

+ down_revision = '2b8f5a6f10a4'

+ 

+ from alembic import op

+ import sqlalchemy as sa

+ 

+ 

+ def upgrade():

+     """ Add the url_badge column to the Elections table. """

+     op.add_column(

+         'elections',

+         sa.Column('url_badge', sa.Unicde(250), nullable=True)

+     )

+ 

+ 

+ def downgrade():

+     """ Drop the url_badge column from the Elections table. """

+     op.drop_column('elections', 'url_badge')

file modified
+41 -10
@@ -39,10 +39,11 @@ 

  from urlparse import urlparse, urljoin  # noqa

  

  import flask  # noqa

+ import munch  # noqa

  

  from fedora.client import AuthError, AppError  # noqa

  from fedora.client.fas2 import AccountSystem  # noqa

- from flask_fas_openid import FAS  # noqa

+ from flask_oidc import OpenIDConnect  # noqa

  

  import fedora_elections.fedmsgshim  # noqa

  import fedora_elections.mail_logging  # noqa
@@ -54,7 +55,7 @@ 

      APP.config.from_envvar('FEDORA_ELECTIONS_CONFIG')

  

  # set up FAS

- FAS = FAS(APP)

+ OIDC = OpenIDConnect(APP, credentials_store=flask.session)

  

  # Set up the logging

  if not APP.debug:
@@ -106,12 +107,22 @@ 

          ref_url.netloc == test_url.netloc

  

  

- def is_admin(user):

+ def is_admin(user, user_groups=None):

      ''' Is the user an elections admin.

      '''

+     if not user_groups:

+         user_groups = []

+ 

      if not user:

          return False

-     if not user.cla_done or len(user.groups) < 1:

+     if not user.cla_done:

+         return False

+ 

+     user_groups = []

+     if is_authenticated() and OIDC.user_loggedin:

+         user_groups = OIDC.user_getfield('groups')

+ 

+     if len(user_groups) < 1:

          return False

  

      admins = APP.config['FEDORA_ELECTIONS_ADMIN_GROUP']
@@ -120,7 +131,7 @@ 

      else:

          admins = set(admins)

  

-     return len(set(user.groups).intersection(admins)) > 0

+     return len(set(user_groups).intersection(admins)) > 0

  

  

  def is_election_admin(user, election_id):
@@ -163,7 +174,7 @@ 

      template).

      '''

      user = None

-     if hasattr(flask.g, 'fas_user'):

+     if is_authenticated() and OIDC.user_loggedin:

          user = flask.g.fas_user

      return dict(is_admin=is_admin(user),

                  version=__version__)
@@ -195,10 +206,25 @@ 

  

  # pylint: disable=W0613

  @APP.before_request

- def set_session():

+ def set_session():  # pragma: no-cover

      """ Set the flask session as permanent. """

      flask.session.permanent = True

  

+     if OIDC.user_loggedin:

+         if not hasattr(flask.session, 'fas_user') or not flask.session.fas_user:

+             flask.session.fas_user = munch.Munch({

+                 'username': OIDC.user_getfield('nickname'),

+                 'email': OIDC.user_getfield('email') or '',

+                 'timezone': OIDC.user_getfield('zoneinfo'),

+                 'cla_done': \

+                     'http://admin.fedoraproject.org/accounts/cla/done' \

+                     in (OIDC.user_getfield('cla') or []),

+             })

+         flask.g.fas_user = flask.session.fas_user

+     else:

+         flask.session.fas_user = None

+         flask.g.fas_user = None

+ 

  

  # pylint: disable=W0613

  @APP.teardown_request
@@ -272,7 +298,10 @@ 

          stats=stats,

          voted=voted,

          evolution_label=evolution_label,

-         evolution_data=evolution_data)

+         evolution_data=evolution_data,

+         candidates=sorted(

+             election.candidates, key=lambda x: x.vote_count, reverse=True),

+     )

  

  

  @APP.route('/archives')
@@ -291,6 +320,7 @@ 

  

  

  @APP.route('/login', methods=('GET', 'POST'))

+ @OIDC.require_login

  def auth_login():

      next_url = None

      if 'next' in flask.request.args:
@@ -307,14 +337,15 @@ 

          if isinstance(groups, basestring):

              groups = [groups]

          groups.extend(models.get_groups(SESSION))

-         return FAS.login(return_url=next_url, groups=groups)

+         return flask.redirect(next_url)

  

  

  @APP.route('/logout')

  def auth_logout():

      if hasattr(flask.g, 'fas_user') and flask.g.fas_user is not None:

-         FAS.logout()

+         OIDC.logout()

          flask.g.fas_user = None

+         flask.session.fas_user = None

          flask.flash('You have been logged out')

      return safe_redirect_back()

  

file modified
+7 -3
@@ -74,6 +74,7 @@ 

              end_date=form.end_date.data,

              seats_elected=form.seats_elected.data,

              embargoed=int(form.embargoed.data),

+             url_badge=form.url_badge.data,

              voting_type=form.voting_type.data,

              max_votes=form.max_votes.data,

              candidates_are_fasusers=int(form.candidates_are_fasusers.data),
@@ -115,7 +116,6 @@ 

          )

  

          flask.flash('Election "%s" added' % election.alias)

-         fedmsgshim.publish(topic="election.new", msg=election)

          return flask.redirect(flask.url_for(

              'admin_view_election', election_alias=election.alias))

      return flask.render_template(
@@ -130,7 +130,11 @@ 

      election = models.Election.get(SESSION, alias=election_alias)

      if not election:

          flask.abort(404)

-     form = forms.ElectionForm(election.id, obj=election)

+     if flask.request.method == 'GET':

+         form = forms.ElectionForm(election.id, obj=election)

+     else:

+         form = forms.ElectionForm(election.id)

+ 

      if form.validate_on_submit():

          form.embargoed.data = int(form.embargoed.data)

          if form.max_votes.data:
@@ -410,7 +414,7 @@ 

                      candidate=candidate.to_json(),

                  )

              )

-         except SQLAlchemyError, err:

+         except SQLAlchemyError as err:

              SESSION.rollback()

              APP.logger.debug('Could not delete candidate')

              APP.logger.exception(err)

@@ -3,7 +3,7 @@ 

  '''

  Fedora elections default configuration.

  '''

- 

+ import os

  from datetime import timedelta

  

  # Set the time after which the session expires
@@ -22,3 +22,9 @@ 

  FAS_USERNAME = ''

  FAS_PASSWORD = ''

  FAS_CHECK_CERT = False

+ 

+ 

+ OIDC_CLIENT_SECRETS = os.path.join(os.path.dirname(

+     os.path.abspath(__file__)), 'client_secrets.json')

+ OIDC_SCOPES = ['openid', 'email', 'profile', 'fedora']

+ OIDC_OPENID_REALM = 'http://localhost:5005/oidc_callback'

file modified
+24 -11
@@ -32,7 +32,7 @@ 

  from fedora_elections import forms

  from fedora_elections import models

  from fedora_elections import (

-     APP, SESSION, is_authenticated, is_admin, is_election_admin,

+     OIDC, APP, SESSION, is_authenticated, is_admin, is_election_admin,

      safe_redirect_back,

  )

  from fedora_elections.utils import build_name_map
@@ -48,11 +48,13 @@ 

              flask.flash(

                  'You must sign the CLA to vote', 'error')

              return safe_redirect_back()

-         elif len(flask.g.fas_user.groups) == 0:

-             flask.flash(

-                 'You need to be in one another group than CLA to vote',

-                 'error')

-             return safe_redirect_back()

+         else:

+             user_groups = OIDC.user_getfield('groups')

+             if len(user_groups) == 0:

+                 flask.flash(

+                     'You need to be in one another group than CLA to vote',

+                     'error')

+                 return safe_redirect_back()

  

          return f(*args, **kwargs)

      return decorated_function
@@ -93,7 +95,8 @@ 

          return election

  

      if election.legal_voters_list:

-         if len(set(flask.g.fas_user.groups).intersection(

+         user_groups = OIDC.user_getfield('groups')

+         if len(set(user_groups).intersection(

                  set(election.legal_voters_list))) == 0:

              flask.flash(

                  'You are not among the groups that are allowed to vote '
@@ -141,6 +144,8 @@ 

          election=election,

          usernamemap=usernamemap,

          stats=stats,

+         candidates=sorted(

+             election.candidates, key=lambda x: x.vote_count, reverse=True)

      )

  

  
@@ -168,7 +173,7 @@ 

                  and candidate.short_name not in ['csrf_token', 'action']

              ]

              process_vote(candidates, election, votes, revote)

-             flask.flash("Your vote has been recorded.  Thank you!")

+             say_thank_you(election)

              return safe_redirect_back()

  

          if form.action.data == 'preview':
@@ -224,7 +229,7 @@ 

                      and candidate.short_name not in ['csrf_token', 'action']

                  ]

                  process_vote(candidates, election, votes, revote, cand_name)

-                 flask.flash("Your vote has been recorded.  Thank you!")

+                 say_thank_you(election)

                  return safe_redirect_back()

  

              if form.action.data == 'preview':
@@ -264,7 +269,7 @@ 

                  and candidate.short_name not in ['csrf_token', 'action']

              ]

              process_vote(candidates, election, votes, revote, value=1)

-             flask.flash("Your vote has been recorded.  Thank you!")

+             say_thank_you(election)

              return safe_redirect_back()

  

          if form.action.data == 'preview':
@@ -302,7 +307,7 @@ 

                  and candidate.short_name not in ['csrf_token', 'action']

              ]

              process_vote(candidates, election, votes, revote, cand_name)

-             flask.flash("Your vote has been recorded.  Thank you!")

+             say_thank_you(election)

              return safe_redirect_back()

  

          if form.action.data == 'preview':
@@ -345,3 +350,11 @@ 

              )

              SESSION.add(new_vote)

          SESSION.commit()

+ 

+ 

+ def say_thank_you(election):

+     thank_you = "Your vote has been recorded.  Thank you!"

+     if election.url_badge:

+         thank_you = thank_you + '<br><a href="' + \

+             election.url_badge + '" target=_new>Claim your I Voted badge</a>.'

+     flask.flash(thank_you)

@@ -13,5 +13,5 @@ 

      try:

          import fedmsg

          fedmsg.publish(*args, **kwargs)

-     except Exception, e:

+     except Exception as e:

          warnings.warn(str(e))

file modified
+7 -1
@@ -74,6 +74,12 @@ 

  

      embargoed = wtforms.BooleanField('Embargo results?', default=True)

  

+     url_badge = wtforms.TextField(

+         'Badge URL (optional)', [

+             wtforms.validators.Optional(),

+             wtforms.validators.URL(),

+             wtforms.validators.Length(max=250)])

+ 

      lgl_voters = wtforms.TextField(

          'Legal voters groups', [wtforms.validators.optional()])

  
@@ -150,7 +156,7 @@ 

              try:

                  title = \

                      FAS2.person_by_username(candidate.name)['human_name']

-             except (KeyError, AuthError), err:

+             except (KeyError, AuthError) as err:

                  APP.logger.debug(err)

          if candidate.url:

              title = '%s <a href="%s">[Info]</a>' % (title, candidate.url)

@@ -86,6 +86,7 @@ 

      seats_elected = sa.Column(sa.Integer, nullable=False, default=1)

      embargoed = sa.Column(sa.Integer, nullable=False, default=0)

      voting_type = sa.Column(sa.Unicode(100), nullable=False, default=u'range')

+     url_badge = sa.Column(sa.Unicode(250), nullable=True)

      max_votes = sa.Column(sa.Integer, nullable=True)

      candidates_are_fasusers = sa.Column(

          sa.Integer, nullable=False, default=0)

@@ -112,6 +112,7 @@ 

            {{ render_bootstrap_textfield_in_row(form.seats_elected) }}

            {{ render_bootstrap_checkbox_in_row(form.candidates_are_fasusers) }}

            {{ render_bootstrap_checkbox_in_row(form.embargoed) }}

+           {{ render_bootstrap_textfield_in_row(form.url_badge) }}

            {{ render_bootstrap_textfield_in_row(form.lgl_voters, after="FAS groups allowed to vote on this election (CLA-done is always required)") }}

            {{ render_bootstrap_textfield_in_row(form.admin_grp, after="FAS groups allowed to view the result despite the embargo") }}

        </fieldset>

@@ -107,41 +107,44 @@ 

      {% endif %}

      <h3> Results </h3>

      <table id="results" class="table">

-         <thead>

-           <tr>

-               <th class="stretch-cell nowrap">Candidate</th>

-               <th class="nowrap" title="Number of votes received">Votes</th>

-               {% if stats['candidate_voters'] %}

-               <th class="nowrap">Voters per candidate</th>

-               <th class="nowrap">Average votes per candidate</th>

-               {% endif %}

-           </tr>

+       <thead>

+         <tr>

+           <th class="stretch-cell nowrap">Candidate</th>

+           <th class="nowrap" title="Number of votes received">Votes</th>

+           {% if stats['candidate_voters'] %}

+           <th class="nowrap">Voters per candidate</th>

+           <th class="nowrap">Average votes per candidate</th>

+           {% endif %}

+         </tr>

        </thead>

  

-       {% for candidate in election.candidates|sort(attribute='vote_count', reverse=True) %}

-       {% if loop.index <= election.seats_elected %}

-       {# If we are below the number of user that will be selected, get the number

-       of votes and the flag to False#}

-       {% set flag = False %}

-       {% set votes = candidate.vote_count %}

-       {% elif loop.index > election.seats_elected and votes > candidate.vote_count and not flag %}

-       {# if we are above the number of user that will be selected (seats

-       available), check if the number of votes for this candidate is lower than

-       the number of votes for the last candidate and if the Flag is False

-       So this takes care of the case where there are 10 seats elected and the 11th

-       candidate has the same score as the 10th one.

-       In this case we would end up with one more person that the number of seats

-       available and we'll need to either find a way to select one over the other

-       or deal with having one more candidate accepted #}

-       {% set flag = True %}

-       {% set lastrow = True %}

-       {% else %}

-       {# we are above the number of seats available, the number of votes is below

-       that of the last candidate above selected and the Flag is True which means

-       we already passed the condition above #}

-       {% set lastrow = False %}

-       {% endif %}

-       <tr class="{% if lastrow == True %}firstout{% endif %} {{ loop.cycle('row_odd', 'row_even') }}">

+       {%- set lastrow = [0] -%}

+       {%- set flag = [0] -%}

+       {% for candidate in candidates %}

+         {% if loop.index <= election.seats_elected %}

+           {# If we are below the number of user that will be selected, get the number

+           of votes and the flag to False#}

+           {%- set _ = flag.append(0) -%}

+         {%- elif loop.index > election.seats_elected

+           and candidates[loop.index -2].vote_count > candidate.vote_count

+           and flag[-1] == 0 -%}

+           {# if we are above the number of user that will be selected (seats

+             available), check if the number of votes for this candidate is lower than

+             the number of votes for the last candidate and if the Flag is False

+             So this takes care of the case where there are 10 seats elected and the 11th

+             candidate has the same score as the 10th one.

+             In this case we would end up with one more person that the number of seats

+             available and we'll need to either find a way to select one over the other

+             or deal with having one more candidate accepted #}

+          {%- set _ = lastrow.append(1) -%}

+          {%- set _ = flag.append(1) -%}

+         {% else %}

+           {# we are above the number of seats available, the number of votes is below

+             that of the last candidate above selected and the Flag is True which means

+             we already passed the condition above #}

+           {% set _ = lastrow.append(0) -%}

+         {% endif %}

+       <tr class="{% if lastrow[-1] == 1 %}firstout{% endif %} {{ loop.cycle('row_odd', 'row_even') }}">

            <td>

              {% if candidate.url %}

                <a href="{{ candidate.url }}">
@@ -154,7 +157,7 @@ 

              {% if candidate.url %}

                </a>

              {% endif %}

-             {% if not flag %}

+             {% if flag[-1] == 0 %}

              <span class="label label-success">Elected</span>

              {% endif %}

            </td>

@@ -21,14 +21,17 @@ 

  The results for the elections are as follows:

  

    # votes |  name

- - --------+----------------------{%

- for candidate in election.candidates|sort(attribute='vote_count', reverse=True) -%}

+ - --------+----------------------

+ {%- set lastrow = [0] -%}

+ {%- set flag = [0] -%}

+ {%- for candidate in candidates -%}

    {% if loop.index <= election.seats_elected -%}

-         {# If we are below the number of user that will be selected,

+         {# If we are below the number of user that will be selected

            get the number of votes and the flag to False -#}

-     {% set flag = False -%}

-     {% set votes = candidate.vote_count -%}

-   {%- elif loop.index > election.seats_elected and votes > candidate.vote_count and not flag -%}

+     {%- set _ = flag.append(0) -%}

+   {%- elif loop.index > election.seats_elected

+         and candidates[loop.index -2].vote_count > candidate.vote_count

+         and flag[-1] == 0 -%}

      {# if we are above the number of user that will be selected (seats

        available), check if the number of votes for this candidate is lower

        than the number of votes for the last candidate and if the Flag is
@@ -38,14 +41,17 @@ 

        In this case we would end up with one more person that the number of

        seats available and we'll need to either find a way to select one

        over the other or deal with having one more candidate accepted -#}

-     {% set flag = True -%}

-     {% set lastrow = True -%}

+     {%- set _ = lastrow.append(1) -%}

+     {%- set _ = flag.append(1) -%}

    {%- else -%}

      {# we are above the number of seats available, the number of votes is

        below that of the last candidate above selected and the Flag is True

        which means we already passed the condition above -#}

-     {% set lastrow = False -%} {%- endif %}{% if lastrow == True %}

- - --------+---------------------- {%- endif %}

+     {% set _ = lastrow.append(0) -%}

+   {%- endif -%}

+   {%- if lastrow[-1] == 1 %}

+ - --------+----------------------

+   {%- endif %}

  {{ candidate.vote_count | rjust(8) }}  | {% if election.candidates_are_fasusers -%}

        {{ usernamemap[candidate.id] }} {%- else -%} {{candidate.name}}

      {%- endif %}

file modified
+1 -3
@@ -1,12 +1,10 @@ 

  # Used for when working from a virtualenv.

  # Use this file by running "$ pip install -r requirements.txt"

  Flask>=0.10

+ flask-oidc

  Flask-wtf

  kitchen

  python-fedora

- python-openid

- python-openid-cla

- python-openid-teams

  SQLAlchemy

  wtforms

  arrow

file modified
+1 -1
@@ -23,7 +23,7 @@ 

      default=False,

      help='Profile the application.')

  parser.add_argument(

-     '--port', '-p', default=5000,

+     '--port', '-p', default=5005,

      help='Port for the flask application.')

  parser.add_argument(

      '--host', default="127.0.0.1",

file modified
+1
@@ -1,4 +1,5 @@ 

  #!/bin/bash

  

+ FEDORA_ELECTIONS_CONFIG=`pwd`/tests/config \

  PYTHONPATH=fedora_elections ./nosetests \

  --with-coverage --cover-erase --cover-package=fedora_elections $*

file modified
+11 -5
@@ -4,13 +4,19 @@ 

  Setup script

  """

  

- # These two lines are needed to run on EL6

- __requires__ = ['SQLAlchemy >= 0.7', 'jinja2 >= 2.4']

- import pkg_resources

+ import os

+ import re

+ from setuptools import setup

  

- from fedora_elections import __version__

+ versionfile = os.path.join(

+     os.path.dirname(__file__), 'fedora_elections', '__init__.py')

  

- from setuptools import setup

+ # Thanks to SQLAlchemy:

+ # https://github.com/zzzeek/sqlalchemy/blob/master/setup.py#L104

+ with open(versionfile) as stream:

+     __version__ = re.compile(

+         r".*__version__ = '(.*?)'", re.S

+     ).match(stream.read()).group(1)

  

  setup(

      name='fedora-elections',

file modified
+25 -2
@@ -20,6 +20,7 @@ 

  

   fedora_elections test script

  """

+ from __future__ import print_function

  

  __requires__ = ['SQLAlchemy >= 0.7']

  import pkg_resources
@@ -57,7 +58,7 @@ 

          req = requests.get('%s/new' % FAITOUT_URL)

          if req.status_code == 200:

              DB_PATH = req.text

-             print 'Using faitout at: %s' % DB_PATH

+             print('Using faitout at: %s' % DB_PATH)

      except:

          pass

  
@@ -66,7 +67,7 @@ 

  

  

  @contextmanager

- def user_set(APP, user):

+ def user_set(APP, user, oidc_id_token=None):

      """ Set the provided user as fas_user in the provided application."""

  

      # Hack used to remove the before_request function set by
@@ -77,6 +78,7 @@ 

  

      def handler(sender, **kwargs):

          g.fas_user = user

+         g.oidc_id_token = oidc_id_token

  

      with appcontext_pushed.connected_to(handler, APP):

          yield
@@ -138,6 +140,27 @@ 

  

          self.app = fedora_elections.APP.test_client()

  

+     def get_wtforms_version(self):

+         """Returns the wtforms version as a tuple."""

+         import wtforms

+         wtforms_v = wtforms.__version__.split('.')

+         for idx, val in enumerate(wtforms_v):

+             try:

+                 val = int(val)

+             except ValueError:

+                 pass

+             wtforms_v[idx] = val

+         return tuple(wtforms_v)

+ 

+     def get_csrf(self, url='/admin/new', output=None):

+         """Retrieve a CSRF token from given URL."""

+         if output is None:

+             output = self.app.get(url)

+             self.assertEqual(output.status_code, 200)

+ 

+         return output.get_data(as_text=True).split(

+             'name="csrf_token" type="hidden" value="')[1].split('">')[0]

+ 

  

  class FakeGroup(object):

      """ Fake object used to make the FakeUser object closer to the

@@ -0,0 +1,12 @@ 

+ {

+ 	"web": {

+ 		"redirect_uris": ["http://localhost:5005/oidc_callback"],

+ 		"token_uri": "https://iddev.fedorainfracloud.org/openidc/Token",

+ 		"auth_uri": "https://iddev.fedorainfracloud.org/openidc/Authorization",

+ 		"client_id": "client_id",

+ 		"client_secret": "client_secret",

+ 		"userinfo_uri": "https://iddev.fedorainfracloud.org/openidc/UserInfo",

+ 		"token_introspection_uri": "https://iddev.fedorainfracloud.org/openidc/TokenInfo",

+ 		"issuer": "https://iddev.fedorainfracloud.org/openidc/"

+ 	}

+ }

file added
+4
@@ -0,0 +1,4 @@ 

+ import os

+ 

+ OIDC_CLIENT_SECRETS = os.path.join(os.path.dirname(

+     os.path.abspath(__file__)), 'client_secrets.json')

file modified
+64 -33
@@ -31,6 +31,7 @@ 

  from datetime import timedelta

  

  import flask

+ from mock import patch, MagicMock

  

  sys.path.insert(0, os.path.join(os.path.dirname(

      os.path.abspath(__file__)), '..'))
@@ -117,18 +118,31 @@ 

              flask.g.fas_user = None

              self.assertFalse(fedora_elections.is_admin(flask.g.fas_user))

  

-             flask.g.fas_user = FakeUser()

-             self.assertFalse(fedora_elections.is_admin(flask.g.fas_user))

+             flask.g.oidc_id_token = 'foobar'

+ 

+             with patch(

+                     'fedora_elections.OIDC.user_getfield',

+                     MagicMock(return_value=['foobar'])):

+                 flask.g.fas_user = FakeUser()

+                 self.assertFalse(fedora_elections.is_admin(flask.g.fas_user))

  

-             flask.g.fas_user = FakeUser(

-                 fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'])

-             self.assertTrue(fedora_elections.is_admin(flask.g.fas_user))

+             with patch(

+                     'fedora_elections.OIDC.user_getfield',

+                     MagicMock(return_value=['elections'])):

+                 flask.g.fas_user = FakeUser(

+                     fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'])

+                 self.assertTrue(

+                     fedora_elections.is_admin(flask.g.fas_user, ['elections']))

  

-             fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'] = [

-                 'sysadmin-main', 'sysadmin-elections']

-             flask.g.fas_user = FakeUser(

-                 fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'])

-             self.assertTrue(fedora_elections.is_admin(flask.g.fas_user))

+             with patch(

+                     'fedora_elections.OIDC.user_getfield',

+                     MagicMock(return_value=['sysadmin-main'])):

+ 

+                 fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'] = [

+                     'sysadmin-main', 'sysadmin-elections']

+                 flask.g.fas_user = FakeUser(['sysadmin-main'])

+                 self.assertTrue(

+                     fedora_elections.is_admin(flask.g.fas_user, ['sysadmin-main']))

  

      def test_is_election_admin(self):

          """ Test the is_election_admin function. """
@@ -140,31 +154,42 @@ 

                  fedora_elections.is_election_admin(

                      flask.g.fas_user, 1)

              )

+ 

+             flask.g.oidc_id_token = 'foobar'

+ 

              flask.g.fas_user = FakeUser()

              self.assertFalse(

                  fedora_elections.is_election_admin(

                      flask.g.fas_user, 1)

              )

-             flask.g.fas_user = FakeUser(

-                 fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'])

-             self.assertTrue(fedora_elections.is_election_admin(

-                 flask.g.fas_user, 1))

+             with patch(

+                     'fedora_elections.OIDC.user_getfield',

+                     MagicMock(return_value=['elections'])):

+                 flask.g.fas_user = FakeUser(

+                     fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'])

+                 self.assertTrue(fedora_elections.is_election_admin(

+                     flask.g.fas_user, 1))

  

          self.setup_db()

  

          with app.test_request_context():

              flask.g.fas_user = FakeUser('testers')

-             # This is user is not an admin for election #1

-             self.assertFalse(

-                 fedora_elections.is_election_admin(

-                     flask.g.fas_user, 1)

-             )

+             flask.g.oidc_id_token = 'foobar'

  

-             # This is user is an admin for election #2

-             self.assertTrue(

-                 fedora_elections.is_election_admin(

-                     flask.g.fas_user, 2)

-             )

+             # This is user is not an admin for election #1

+             with patch(

+                     'fedora_elections.OIDC.user_getfield',

+                     MagicMock(return_value=['foobar'])):

+                 self.assertFalse(

+                     fedora_elections.is_election_admin(

+                         flask.g.fas_user, 1)

+                 )

+ 

+                 # This is user is an admin for election #2

+                 self.assertTrue(

+                     fedora_elections.is_election_admin(

+                         flask.g.fas_user, 2)

+                 )

  

      def test_is_safe_url(self):

          """ Test the is_safe_url function. """
@@ -182,17 +207,23 @@ 

          with app.test_request_context():

              flask.g.fas_user = FakeUser(['gitr2spec'])

              output = self.app.get('/login')

-             self.assertEqual(output.status_code, 200)

+             self.assertEqual(output.status_code, 302)

+             self.assertIn(

+                 'https://iddev.fedorainfracloud.org/openidc/Authorization?',

+                 output.data)

  

              output = self.app.get('/login?next=http://localhost/')

-             self.assertEqual(output.status_code, 200)

- 

-         self.setup_db()

-         user = FakeUser([], username='pingou')

-         with user_set(fedora_elections.APP, user):

-             output = self.app.get('/login', follow_redirects=True)

-             self.assertEqual(output.status_code, 200)

-             self.assertTrue('<title>Fedora elections</title>' in output.data)

+             self.assertEqual(output.status_code, 302)

+             self.assertIn(

+                 'https://iddev.fedorainfracloud.org/openidc/Authorization?',

+                 output.data)

+ 

+         # self.setup_db()

+         # user = FakeUser([], username='pingou')

+         # with user_set(fedora_elections.APP, user):

+             # output = self.app.get('/login', follow_redirects=True)

+             # self.assertEqual(output.status_code, 200)

+             # self.assertTrue('<title>Fedora elections</title>' in output.data)

  

      def test_auth_logout(self):

          """ Test the auth_logout function. """

file modified
+967 -736
@@ -32,6 +32,7 @@ 

  from datetime import timedelta

  

  import flask

+ from mock import patch, MagicMock

  

  sys.path.insert(0, os.path.join(os.path.dirname(

      os.path.abspath(__file__)), '..'))
@@ -49,17 +50,39 @@ 

          user = FakeUser(

              fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'],

              username='toshio')

-         with user_set(fedora_elections.APP, user):

-             output = self.app.get('/admin/election_test/')

-             self.assertEqual(output.status_code, 404)

  

+         with user_set(fedora_elections.APP, user, oidc_id_token='foobar'):

+             with patch(

+                     'fedora_elections.OIDC.user_getfield',

+                     MagicMock(return_value=['elections'])):

+                 output = self.app.get('/admin/election_test/')

+                 self.assertEqual(output.status_code, 404)

+ 

+         self.setup_db()

+ 

+         with user_set(fedora_elections.APP, user, oidc_id_token='foobar'):

+             with patch(

+                     'fedora_elections.OIDC.user_getfield',

+                     MagicMock(return_value=['elections'])):

+                 output = self.app.get('/admin/test_election/')

+                 self.assertEqual(output.status_code, 200)

+                 self.assertTrue('Candidates <span class="label label-pill label-default">3</span>' in output.data)

+ 

+     def test_admin_no_cla(self):

+         """ Test the admin_new_election function. """

          self.setup_db()

  

-         with user_set(fedora_elections.APP, user):

-             output = self.app.get('/admin/test_election/')

-             self.assertEqual(output.status_code, 200)

-             self.assertTrue('Candidates <span class="label label-pill label-default">3</span>' in output.data)

+         user = FakeUser(

+             fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'],

+             username='toshio')

  

+         user.cla_done = False

+         with user_set(fedora_elections.APP, user, oidc_id_token='foobar'):

+             with patch(

+                     'fedora_elections.OIDC.user_getfield',

+                     MagicMock(return_value=['elections'])):

+                 output = self.app.get('/admin/new')

+                 self.assertEqual(output.status_code, 403)

  

      def test_admin_new_election(self):

          """ Test the admin_new_election function. """
@@ -68,339 +91,421 @@ 

          user = FakeUser(

              fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'],

              username='toshio')

-         with user_set(fedora_elections.APP, user):

-             output = self.app.get('/admin/new')

-             self.assertEqual(output.status_code, 200)

-             self.assertTrue(

-                 '<h2>Create new election</h2>' in output.data)

-             self.assertTrue(

-                 'input class="form-control" id="shortdesc" name="shortdesc" type="text"'

-                 in output.data)

- 

-             # No csrf provided

-             data = {

-                 'alias': 'new_election',

-                 'shortdesc': 'new election shortdesc',

-                 'description': 'new election description',

-                 'voting_type': 'simple',

-                 'url': 'https://fedoraproject.org',

-                 'start_date': TODAY + timedelta(days=2),

-                 'end_date': TODAY + timedelta(days=4),

-                 'seats_elected': '2',

-                 'candidates_are_fasusers': False,

-                 'embargoed': True,

-             }

- 

-             output = self.app.post('/admin/new', data=data)

-             self.assertEqual(output.status_code, 200)

-             self.assertTrue(

-                 '<h2>Create new election</h2>' in output.data)

-             self.assertTrue(

-                 'input class="form-control" id="shortdesc" name="shortdesc" type="text"'

-                 in output.data)

- 

-             csrf_token = output.data.split(

-                 'name="csrf_token" type="hidden" value="')[1].split('">')[0]

- 

-             # Description missing

-             data = {

-                 'alias': 'new_election',

-                 'shortdesc': 'new election shortdesc',

-                 'voting_type': 'simple',

-                 'url': 'https://fedoraproject.org',

-                 'start_date': TODAY + timedelta(days=2),

-                 'end_date': TODAY + timedelta(days=4),

-                 'seats_elected': '2',

-                 'candidates_are_fasusers': False,

-                 'embargoed': True,

-                 'csrf_token': csrf_token,

-             }

- 

-             output = self.app.post('/admin/new', data=data)

-             self.assertEqual(output.status_code, 200)

-             self.assertTrue(

-                 '<h2>Create new election</h2>' in output.data)

-             self.assertTrue(

-                 'input class="form-control" id="shortdesc" name="shortdesc" type="text"'

-                 in output.data)

-             self.assertTrue(

-                 '<div class="form-control-feedback">This field is required.</div>'

-                 in output.data)

- 

-             # Invalid alias

-             data = {

-                 'alias': 'new',

-                 'shortdesc': 'new election shortdesc',

-                 'description': 'new election description',

-                 'voting_type': 'simple',

-                 'url': 'https://fedoraproject.org',

-                 'start_date': TODAY + timedelta(days=2),

-                 'end_date': TODAY + timedelta(days=4),

-                 'seats_elected': 2,

-                 'candidates_are_fasusers': False,

-                 'embargoed': True,

-                 'csrf_token': csrf_token,

-             }

- 

-             output = self.app.post('/admin/new', data=data)

-             self.assertEqual(output.status_code, 200)

-             self.assertTrue(

-                 '<h2>Create new election</h2>' in output.data)

-             self.assertTrue(

-                 'input class="form-control" id="shortdesc" name="shortdesc" type="text"'

-                 in output.data)

-             self.assertTrue(

-                 '<div class="form-control-feedback">The alias cannot be <code>new</code>.</div>'

-                 in output.data)

- 

-             # Invalid: end_date earlier than start_date

-             data = {

-                 'alias': 'new_election',

-                 'shortdesc': 'new election shortdesc',

-                 'description': 'new election description',

-                 'voting_type': 'simple',

-                 'url': 'https://fedoraproject.org',

-                 'start_date': TODAY + timedelta(days=6),

-                 'end_date': TODAY + timedelta(days=4),

-                 'seats_elected': 2,

-                 'candidates_are_fasusers': False,

-                 'embargoed': True,

-                 'csrf_token': csrf_token,

-             }

- 

-             output = self.app.post('/admin/new', data=data)

-             self.assertEqual(output.status_code, 200)

-             self.assertTrue(

-                 '<h2>Create new election</h2>' in output.data)

-             self.assertTrue(

-                 'input class="form-control" id="shortdesc" name="shortdesc" type="text"'

-                 in output.data)

-             self.assertTrue(

-                 'class="form-control-feedback">End date must be later than start date.</div>'

-                 in output.data)

- 

-             # Invalid: alias already taken

-             data = {

-                 'alias': 'test_election',

-                 'shortdesc': 'new election shortdesc',

-                 'description': 'new election description',

-                 'voting_type': 'simple',

-                 'url': 'https://fedoraproject.org',

-                 'start_date': TODAY + timedelta(days=6),

-                 'end_date': TODAY + timedelta(days=4),

-                 'seats_elected': 2,

-                 'candidates_are_fasusers': False,

-                 'embargoed': True,

-                 'csrf_token': csrf_token,

-             }

- 

-             output = self.app.post('/admin/new', data=data)

-             self.assertEqual(output.status_code, 200)

-             self.assertTrue(

-                 '<h2>Create new election</h2>' in output.data)

-             self.assertTrue(

-                 'input class="form-control" id="shortdesc" name="shortdesc" type="text"'

-                 in output.data)

-             self.assertTrue(

-                 '<div class="form-control-feedback">There is already another election with '

-                 'this alias.</div>' in output.data)

- 

-             # Invalid: shortdesc already taken

-             data = {

-                 'alias': 'new_election',

-                 'shortdesc': 'test election shortdesc',

-                 'description': 'new election description',

-                 'voting_type': 'simple',

-                 'url': 'https://fedoraproject.org',

-                 'start_date': TODAY + timedelta(days=6),

-                 'end_date': TODAY + timedelta(days=4),

-                 'seats_elected': 2,

-                 'candidates_are_fasusers': False,

-                 'embargoed': True,

-                 'csrf_token': csrf_token,

-             }

- 

-             output = self.app.post('/admin/new', data=data)

-             self.assertEqual(output.status_code, 200)

-             self.assertTrue(

-                 '<h2>Create new election</h2>' in output.data)

-             self.assertTrue(

-                 'input class="form-control" id="shortdesc" name="shortdesc" type="text"'

-                 in output.data)

-             self.assertTrue(

-                 '<div class="form-control-feedback">There is already another election with '

-                 'this summary.</div>' in output.data)

- 

-             # All good  -  max_votes is ignored as it is not a integer

-             data = {

-                 'alias': 'new_election',

-                 'shortdesc': 'new election shortdesc',

-                 'description': 'new election description',

-                 'voting_type': 'simple',

-                 'url': 'https://fedoraproject.org',

-                 'start_date': TODAY + timedelta(days=2),

-                 'end_date': TODAY + timedelta(days=4),

-                 'seats_elected': 2,

-                 'candidates_are_fasusers': False,

-                 'embargoed': True,

-                 'admin_grp': 'testers, sysadmin-main,,',

-                 'lgl_voters': 'testers, packager,,',

-                 'max_votes': 'wrong',

-                 'csrf_token': csrf_token,

-             }

- 

-             output = self.app.post(

-                 '/admin/new', data=data, follow_redirects=True)

-             self.assertEqual(output.status_code, 200)

-             self.assertTrue(

-                 'Election "new_election" added'

-                 in output.data)

-             self.assertTrue(

-                 'There are no candidates.' in output.data)

-             self.assertTrue(

-                 '<input class="form-control" id="admin_grp" name="admin_grp" type="text" value="sysadmin-main, testers">'

-                 in output.data)

-             self.assertTrue(

-                 '<input class="form-control" id="lgl_voters" name="lgl_voters" type="text" value="packager, testers">'

-                 in output.data)

-             self.assertTrue(

-                 '<input class="form-control" id="max_votes" name="max_votes" type="text" value="">'

-                 in output.data)

- 

-             # All good  -  max_votes is ignored as it is not a integer

-             data = {

-                 'alias': 'new_election2',

-                 'shortdesc': 'new election2 shortdesc',

-                 'description': 'new election2 description',

-                 'voting_type': 'simple',

-                 'url': 'https://fedoraproject.org',

-                 'start_date': TODAY + timedelta(days=2),

-                 'end_date': TODAY + timedelta(days=4),

-                 'seats_elected': 2,

-                 'candidates_are_fasusers': False,

-                 'embargoed': True,

-                 'admin_grp': 'testers, , sysadmin-main,,',

-                 'lgl_voters': 'testers, packager,,,',

-                 'csrf_token': csrf_token,

-             }

- 

-             output = self.app.post(

-                 '/admin/new', data=data, follow_redirects=True)

-             self.assertEqual(output.status_code, 200)

-             self.assertTrue(

-                 'Election "new_election2" added'

-                 in output.data)

-             self.assertTrue(

-                 'There are no candidates.' in output.data)

-             self.assertTrue(

-                 '<input class="form-control" id="admin_grp" name="admin_grp" type="text" value="sysadmin-main, testers">'

-                 in output.data)

-             self.assertTrue(

-                 '<input class="form-control" id="lgl_voters" name="lgl_voters" type="text" value="packager, testers">'

-                 in output.data)

-             self.assertTrue(

-                 '<input class="form-control" id="max_votes" name="max_votes" type="text" value="">'

-                 in output.data)

+ 

+         with user_set(fedora_elections.APP, user, oidc_id_token='foobar'):

+             with patch(

+                     'fedora_elections.OIDC.user_getfield',

+                     MagicMock(return_value=['elections'])):

+                 output = self.app.get('/admin/new')

+                 self.assertEqual(output.status_code, 200)

+                 self.assertTrue(

+                     '<h2>Create new election</h2>' in output.data)

+                 if self.get_wtforms_version() >= (2, 2):

+                     self.assertIn(

+                         'input class="form-control" id="shortdesc" '

+                         'name="shortdesc" required type="text" ', output.data)

+                 else:

+                     self.assertIn(

+                         'input class="form-control" id="shortdesc" '

+                         'name="shortdesc" type="text" ', output.data)

+ 

+                 # No csrf provided

+                 data = {

+                     'alias': 'new_election',

+                     'shortdesc': 'new election shortdesc',

+                     'description': 'new election description',

+                     'voting_type': 'simple',

+                     'url': 'https://fedoraproject.org',

+                     'start_date': TODAY + timedelta(days=2),

+                     'end_date': TODAY + timedelta(days=4),

+                     'seats_elected': '2',

+                     'candidates_are_fasusers': False,

+                     'embargoed': True,

+                 }

+ 

+                 output = self.app.post('/admin/new', data=data)

+                 self.assertEqual(output.status_code, 200)

+                 self.assertTrue(

+                     '<h2>Create new election</h2>' in output.data)

+                 if self.get_wtforms_version() >= (2, 2):

+                     self.assertIn(

+                         'input class="form-control" id="shortdesc" '

+                         'name="shortdesc" required type="text" ', output.data)

+                 else:

+                     self.assertIn(

+                         'input class="form-control" id="shortdesc" '

+                         'name="shortdesc" type="text" ', output.data)

+ 

+                 csrf_token = self.get_csrf(output=output)

+ 

+                 # Description missing

+                 data = {

+                     'alias': 'new_election',

+                     'shortdesc': 'new election shortdesc',

+                     'voting_type': 'simple',

+                     'url': 'https://fedoraproject.org',

+                     'start_date': TODAY + timedelta(days=2),

+                     'end_date': TODAY + timedelta(days=4),

+                     'seats_elected': '2',

+                     'candidates_are_fasusers': False,

+                     'embargoed': True,

+                     'csrf_token': csrf_token,

+                 }

+ 

+                 output = self.app.post('/admin/new', data=data)

+                 self.assertEqual(output.status_code, 200)

+                 self.assertTrue(

+                     '<h2>Create new election</h2>' in output.data)

+                 if self.get_wtforms_version() >= (2, 2):

+                     self.assertIn(

+                         'input class="form-control" id="shortdesc" '

+                         'name="shortdesc" required type="text" ', output.data)

+                 else:

+                     self.assertIn(

+                         'input class="form-control" id="shortdesc" '

+                         'name="shortdesc" type="text" ', output.data)

+                 self.assertTrue(

+                     '<div class="form-control-feedback">This field is required.</div>'

+                     in output.data)

+ 

+                 # Invalid alias

+                 data = {

+                     'alias': 'new',

+                     'shortdesc': 'new election shortdesc',

+                     'description': 'new election description',

+                     'voting_type': 'simple',

+                     'url': 'https://fedoraproject.org',

+                     'start_date': TODAY + timedelta(days=2),

+                     'end_date': TODAY + timedelta(days=4),

+                     'seats_elected': 2,

+                     'candidates_are_fasusers': False,

+                     'embargoed': True,

+                     'csrf_token': csrf_token,

+                 }

+ 

+                 output = self.app.post('/admin/new', data=data)

+                 self.assertEqual(output.status_code, 200)

+                 self.assertTrue(

+                     '<h2>Create new election</h2>' in output.data)

+                 if self.get_wtforms_version() >= (2, 2):

+                     self.assertIn(

+                         'input class="form-control" id="shortdesc" '

+                         'name="shortdesc" required type="text" ', output.data)

+                 else:

+                     self.assertIn(

+                         'input class="form-control" id="shortdesc" '

+                         'name="shortdesc" type="text" ', output.data)

+                 self.assertTrue(

+                     '<div class="form-control-feedback">The alias cannot be <code>new</code>.</div>'

+                     in output.data)

+ 

+                 # Invalid: end_date earlier than start_date

+                 data = {

+                     'alias': 'new_election',

+                     'shortdesc': 'new election shortdesc',

+                     'description': 'new election description',

+                     'voting_type': 'simple',

+                     'url': 'https://fedoraproject.org',

+                     'start_date': TODAY + timedelta(days=6),

+                     'end_date': TODAY + timedelta(days=4),

+                     'seats_elected': 2,

+                     'candidates_are_fasusers': False,

+                     'embargoed': True,

+                     'csrf_token': csrf_token,

+                 }

+ 

+                 output = self.app.post('/admin/new', data=data)

+                 self.assertEqual(output.status_code, 200)

+                 self.assertTrue(

+                     '<h2>Create new election</h2>' in output.data)

+                 if self.get_wtforms_version() >= (2, 2):

+                     self.assertIn(

+                         'input class="form-control" id="shortdesc" '

+                         'name="shortdesc" required type="text" ', output.data)

+                 else:

+                     self.assertIn(

+                         'input class="form-control" id="shortdesc" '

+                         'name="shortdesc" type="text" ', output.data)

+                 self.assertTrue(

+                     'class="form-control-feedback">End date must be later than start date.</div>'

+                     in output.data)

+ 

+                 # Invalid: alias already taken

+                 data = {

+                     'alias': 'test_election',

+                     'shortdesc': 'new election shortdesc',

+                     'description': 'new election description',

+                     'voting_type': 'simple',

+                     'url': 'https://fedoraproject.org',

+                     'start_date': TODAY + timedelta(days=6),

+                     'end_date': TODAY + timedelta(days=4),

+                     'seats_elected': 2,

+                     'candidates_are_fasusers': False,

+                     'embargoed': True,

+                     'csrf_token': csrf_token,

+                 }

+ 

+                 output = self.app.post('/admin/new', data=data)

+                 self.assertEqual(output.status_code, 200)

+                 self.assertTrue(

+                     '<h2>Create new election</h2>' in output.data)

+                 if self.get_wtforms_version() >= (2, 2):

+                     self.assertIn(

+                         'input class="form-control" id="shortdesc" '

+                         'name="shortdesc" required type="text" ', output.data)

+                 else:

+                     self.assertIn(

+                         'input class="form-control" id="shortdesc" '

+                         'name="shortdesc" type="text" ', output.data)

+                 self.assertTrue(

+                     '<div class="form-control-feedback">There is already another election with '

+                     'this alias.</div>' in output.data)

+ 

+                 # Invalid: shortdesc already taken

+                 data = {

+                     'alias': 'new_election',

+                     'shortdesc': 'test election shortdesc',

+                     'description': 'new election description',

+                     'voting_type': 'simple',

+                     'url': 'https://fedoraproject.org',

+                     'start_date': TODAY + timedelta(days=6),

+                     'end_date': TODAY + timedelta(days=4),

+                     'seats_elected': 2,

+                     'candidates_are_fasusers': False,

+                     'embargoed': True,

+                     'csrf_token': csrf_token,

+                 }

+ 

+                 output = self.app.post('/admin/new', data=data)

+                 self.assertEqual(output.status_code, 200)

+                 self.assertTrue(

+                     '<h2>Create new election</h2>' in output.data)

+                 if self.get_wtforms_version() >= (2, 2):

+                     self.assertIn(

+                         'input class="form-control" id="shortdesc" '

+                         'name="shortdesc" required type="text" ', output.data)

+                 else:

+                     self.assertIn(

+                         'input class="form-control" id="shortdesc" '

+                         'name="shortdesc" type="text" ', output.data)

+                 self.assertTrue(

+                     '<div class="form-control-feedback">There is already another election with '

+                     'this summary.</div>' in output.data)

+ 

+                 # All good  -  max_votes is ignored as it is not a integer

+                 data = {

+                     'alias': 'new_election',

+                     'shortdesc': 'new election shortdesc',

+                     'description': 'new election description',

+                     'voting_type': 'simple',

+                     'url': 'https://fedoraproject.org',

+                     'start_date': TODAY + timedelta(days=2),

+                     'end_date': TODAY + timedelta(days=4),

+                     'seats_elected': 2,

+                     'candidates_are_fasusers': False,

+                     'embargoed': True,

+                     'admin_grp': 'testers, sysadmin-main,,',

+                     'lgl_voters': 'testers, packager,,',

+                     'max_votes': 'wrong',

+                     'csrf_token': csrf_token,

+                 }

+ 

+                 output = self.app.post(

+                     '/admin/new', data=data, follow_redirects=True)

+                 self.assertEqual(output.status_code, 200)

+                 self.assertTrue(

+                     'Election "new_election" added'

+                     in output.data)

+                 self.assertTrue(

+                     'There are no candidates.' in output.data)

+                 self.assertIn(

+                     'input class="form-control" id="admin_grp" '

+                     'name="admin_grp" type="text" '

+                     'value="sysadmin-main, testers">', output.data)

+                 self.assertIn(

+                     'input class="form-control" id="lgl_voters" '

+                     'name="lgl_voters" type="text" '

+                     'value="packager, testers">', output.data)

+                 self.assertIn(

+                     'input class="form-control" id="max_votes" '

+                     'name="max_votes" type="text" '

+                     'value="">', output.data)

+ 

+                 # All good  -  max_votes is ignored as it is not a integer

+                 data = {

+                     'alias': 'new_election2',

+                     'shortdesc': 'new election2 shortdesc',

+                     'description': 'new election2 description',

+                     'voting_type': 'simple',

+                     'url': 'https://fedoraproject.org',

+                     'start_date': TODAY + timedelta(days=2),

+                     'end_date': TODAY + timedelta(days=4),

+                     'seats_elected': 2,

+                     'candidates_are_fasusers': False,

+                     'embargoed': True,

+                     'admin_grp': 'testers, , sysadmin-main,,',

+                     'lgl_voters': 'testers, packager,,,',

+                     'csrf_token': csrf_token,

+                 }

+ 

+                 output = self.app.post(

+                     '/admin/new', data=data, follow_redirects=True)

+                 self.assertEqual(output.status_code, 200)

+                 self.assertTrue(

+                     'Election "new_election2" added'

+                     in output.data)

+                 self.assertTrue(

+                     'There are no candidates.' in output.data)

+                 self.assertIn(

+                     'input class="form-control" id="admin_grp" '

+                     'name="admin_grp" type="text" '

+                     'value="sysadmin-main, testers">', output.data)

+                 self.assertIn(

+                     'input class="form-control" id="lgl_voters" '

+                     'name="lgl_voters" type="text" '

+                     'value="packager, testers">', output.data)

+                 self.assertIn(

+                     'input class="form-control" id="max_votes" '

+                     'name="max_votes" type="text" '

+                     'value="">', output.data)

  

      def test_admin_edit_election(self):

          """ Test the admin_edit_election function. """

          user = FakeUser(

              fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'],

              username='toshio')

-         with user_set(fedora_elections.APP, user):

-             output = self.app.get('/admin/test_election/')

-             self.assertEqual(output.status_code, 404)

+ 

+         with user_set(fedora_elections.APP, user, oidc_id_token='foobar'):

+             with patch(

+                     'fedora_elections.OIDC.user_getfield',

+                     MagicMock(return_value=['elections'])):

+                 output = self.app.get('/admin/test_election/')

+                 self.assertEqual(output.status_code, 404)

  

          self.setup_db()

  

-         with user_set(fedora_elections.APP, user):

-             output = self.app.get('/admin/test_election/')

-             self.assertEqual(output.status_code, 200)

-             self.assertTrue(

-                 'Election Details' in output.data)

-             self.assertTrue(

-                 'input class="form-control" id="shortdesc" name="shortdesc" type="text"'

-                 in output.data)

- 

-             data = {

-                 'alias': 'test_election',

-                 'shortdesc': 'test election shortdesc',

-                 'description': 'test election description',

-                 'voting_type': 'simple',

-                 'url': 'https://fedoraproject.org',

-                 'start_date': TODAY - timedelta(days=10),

-                 'end_date': TODAY - timedelta(days=8),

-                 'seats_elected': '2',

-                 'candidates_are_fasusers': False,

-                 'embargoed': False,

-             }

- 

-             output = self.app.post('/admin/test_election/', data=data)

-             self.assertEqual(output.status_code, 200)

-             self.assertTrue(

-                 'Election Details' in output.data)

-             self.assertTrue(

-                 'input class="form-control" id="shortdesc" name="shortdesc" type="text"'

-                 in output.data)

- 

-             csrf_token = output.data.split(

-                 'name="csrf_token" type="hidden" value="')[1].split('">')[0]

- 

-             data = {

-                 'alias': 'test_election',

-                 'shortdesc': 'test election shortdesc',

-                 'voting_type': 'simple',

-                 'url': 'https://fedoraproject.org',

-                 'start_date': TODAY - timedelta(days=10),

-                 'end_date': TODAY - timedelta(days=8),

-                 'seats_elected': '2',

-                 'candidates_are_fasusers': False,

-                 'embargoed': False,

-                 'csrf_token': csrf_token,

-             }

- 

-             output = self.app.post('/admin/test_election/', data=data)

-             self.assertEqual(output.status_code, 200)

-             self.assertTrue(

-                 'Election Details' in output.data)

-             self.assertTrue(

-                 'input class="form-control" id="shortdesc" name="shortdesc" type="text"'

-                 in output.data)

-             self.assertTrue(

-                 '<div class="form-control-feedback">This field is required.</div>'

-                 in output.data)

- 

-             # Check election before edit

-             output = self.app.get('/admin/test_election/')

-             self.assertEqual(output.status_code, 200)

-             self.assertTrue('Candidates <span class="label label-pill label-default">3</span>' in output.data)

-             self.assertTrue('<input class="form-control" id="seats_elected" name="seats_elected" type="text" value="1">' in output.data)

- 

-             data = {

-                 'alias': 'test_election',

-                 'shortdesc': 'test election shortdesc',

-                 'description': 'test election description',

-                 'voting_type': 'simple',

-                 'url': 'https://fedoraproject.org',

-                 'start_date': TODAY - timedelta(days=10),

-                 'end_date': TODAY - timedelta(days=8),

-                 'seats_elected': '2',

-                 'candidates_are_fasusers': False,

-                 'embargoed': False,

-                 'max_votes': 'wrong',

-                 'csrf_token': csrf_token,

-             }

- 

-             output = self.app.post(

-                 '/admin/test_election/', data=data, follow_redirects=True)