Пример #1
0
    def test_cookie_contains_password(self):
        self.contest.ip_autologin = False

        # Cookies are of no use if one cannot login by password.
        self.contest.allow_password_authentication = False
        self.assertFailure()

        # The cookie works with all methods as it holds the plaintext password.
        self.contest.allow_password_authentication = True
        self.user.password = hash_password("mypass", method="bcrypt")
        self.assertSuccessAndCookieRefreshed()

        self.user.password = hash_password("mypass", method="plaintext")
        self.assertSuccessAndCookieRefreshed()

        # Cookies contain the password, which is validated every time.
        self.user.password = build_password("newpass")
        self.assertFailure()

        # Contest-specific passwords take precedence over global ones.
        self.participation.password = build_password("mypass")
        self.assertSuccessAndCookieRefreshed()

        # And they do so in the negative case too.
        self.user.password = build_password("mypass")
        self.participation.password = build_password("newpass")
        self.assertFailure()
Пример #2
0
    def test_cookie_contains_password(self):
        self.contest.ip_autologin = False

        # Cookies are of no use if one cannot login by password.
        self.contest.allow_password_authentication = False
        self.assertFailure()

        # The cookie works with all methods as it holds the plaintext password.
        self.contest.allow_password_authentication = True
        self.user.password = hash_password("mypass", method="bcrypt")
        self.assertSuccessAndCookieRefreshed()

        self.user.password = hash_password("mypass", method="plaintext")
        self.assertSuccessAndCookieRefreshed()

        # Cookies contain the password, which is validated every time.
        self.user.password = build_password("newpass")
        self.assertFailure()

        # Contest-specific passwords take precedence over global ones.
        self.participation.password = build_password("mypass")
        self.assertSuccessAndCookieRefreshed()

        # And they do so in the negative case too.
        self.user.password = build_password("mypass")
        self.participation.password = build_password("newpass")
        self.assertFailure()
Пример #3
0
    def test_no_participation_for_user_in_contest(self):
        other_contest = self.add_contest(allow_password_authentication=True)
        other_user = self.add_user(
            username="******", password=build_password("mypass"))
        self.add_participation(contest=other_contest, user=other_user)

        self.assertFailure("myotheruser", "mypass", "127.0.0.1")
Пример #4
0
def update_password(username, password, method, is_hashed):
    logger.info("Updating user password in the database.")

    if password is None:
        return False
    # shell will interfere with special characters, so a hashed string must be protected by
    # single quotes. We must remove them
    if password[0] == "'":
        password[0] = ' '
        if password[-1] == "'":
            password[-1] = ' '
        password = password.strip()

    if is_hashed:
        stored_password = build_password(password, method)
    else:
        stored_password = hash_password(password, method)

    with SessionGen() as session:
        user = session.query(User)\
            .filter(User.username == username).first()
        if user is None:
            logger.error("User %s does not exist.", username)
            return False

        session.query(User).filter(User.username == username).\
            update({"password": stored_password}, synchronize_session="fetch")
        session.commit()

    logger.info("User %s password updated, method=%s. " % (username, method))
    return True
Пример #5
0
    def test_no_participation_for_user_in_contest(self):
        other_contest = self.add_contest(allow_password_authentication=True)
        other_user = self.add_user(
            username="******", password=build_password("mypass"))
        self.add_participation(contest=other_contest, user=other_user)

        self.assertFailure("myotheruser", "mypass", "127.0.0.1")
Пример #6
0
 def setUp(self):
     super(TestValidateLogin, self).setUp()
     self.timestamp = make_datetime()
     self.contest = self.add_contest(allow_password_authentication=True)
     self.user = self.add_user(
         username="******", password=build_password("mypass"))
     self.participation = self.add_participation(
         contest=self.contest, user=self.user)
Пример #7
0
    def test_invalid_password_in_database(self):
        self.contest.ip_autologin = False
        self.contest.allow_password_authentication = True
        self.user.password = "******"
        self.assertFailure()

        self.user.password = build_password("mypass")
        self.participation.password = "******"
        self.assertFailure()
Пример #8
0
    def test_invalid_password_in_database(self):
        self.contest.ip_autologin = False
        self.contest.allow_password_authentication = True
        self.user.password = "******"
        self.assertFailure()

        self.user.password = build_password("mypass")
        self.participation.password = "******"
        self.assertFailure()
Пример #9
0
    def post(self):
        try:
            username = self.get_argument("username")
            email = self.get_argument("email")

            if not 1 <= len(username):
                raise ValueError()
            if not 1 <= len(email):
                raise ValueError()
        except (tornado.web.MissingArgumentError, ValueError):
            raise tornado.web.HTTPError(400)

        # Check that the username exists
        tot_users = self.sql_session.query(User)\
                        .filter(User.username == username)\
                        .filter(User.email == email)\
                        .count()
        if tot_users == 0:
            # HTTP 409: Conflict
            raise tornado.web.HTTPError(409)

        if not hasattr(config, "saco_admin_email_password"):
            logger.error(
                "'saco_admin_email_password' not defined in cms.conf - can't email new password"
            )
            return

        pwd = generate_random_password()

        self.sql_session.query(User)\
                        .filter(User.username == username)\
                        .filter(User.email == email)\
                        .update({'password': build_password(pwd)})
        self.sql_session.commit()

        sender = '*****@*****.**'
        receivers = [email]
        message = password_reset_email_format.format(sender, email, pwd)

        result = 'successful'
        try:
            s = smtplib.SMTP('smtp.saco-evaluator.org.za', 587)
            s.login('*****@*****.**',
                    config.saco_admin_email_password)
            s.sendmail(sender, receivers + [sender], message)
        except smtplib.SMTPException as e:
            logger.error('Unable to send email:', e)
            result = 'unsuccessful'

        if hasattr(config, 'test_logs_dir'):
            with open(os.path.join(config.test_logs_dir, 'pwd_reset_requests'),
                      'a') as f:
                time = datetime.datetime.now().strftime('%y-%m-%d %H:%M')
                f.write('{} {} {} {}\n'.format(username, email, time, result))
        else:
            logger.error(
                "test_logs_dir undefined in cms.conf, can't write logs")
Пример #10
0
class User(Base):
    """Class to store a user.

    """

    __tablename__ = 'users'

    # Auto increment primary key.
    id = Column(
        Integer,
        primary_key=True)

    # Real name (human readable) of the user.
    first_name = Column(
        Unicode,
        nullable=False)
    last_name = Column(
        Unicode,
        nullable=False)

    # Username and password to log in the CWS.
    username = Column(
        Unicode,
        CodenameConstraint("username"),
        nullable=False,
        unique=True)
    password = Column(
        Unicode,
        nullable=False,
        default=lambda: build_password(generate_random_password()))

    # Email for any communications in case of remote contest.
    email = Column(
        Unicode,
        nullable=True)

    # Timezone for the user. All timestamps in CWS will be shown using
    # the timezone associated to the logged-in user or (if it's None
    # or an invalid string) the timezone associated to the contest or
    # (if it's None or an invalid string) the local timezone of the
    # server. This value has to be a string like "Europe/Rome",
    # "Australia/Sydney", "America/New_York", etc.
    timezone = Column(
        Unicode,
        nullable=True)

    # The language codes accepted by this user (from the "most
    # preferred" to the "least preferred"). If in a contest there is a
    # statement available in some of these languages, then the most
    # preferred of them will be highlighted.
    # FIXME: possibly move it to Participation and change it back to
    # primary_statements
    preferred_languages = Column(
        ARRAY(String),
        nullable=False,
        default=[])
Пример #11
0
def add_participation(username, contest_id, ip, delay_time, extra_time,
                      password, method, is_hashed, team_code, hidden,
                      unrestricted):
    logger.info("Creating the user's participation in the database.")
    delay_time = delay_time if delay_time is not None else 0
    extra_time = extra_time if extra_time is not None else 0

    if hidden:
        logger.warning("The participation will be hidden")
    if unrestricted:
        logger.warning("The participation will be unrestricted")

    try:
        with SessionGen() as session:
            user = \
                session.query(User).filter(User.username == username).first()
            if user is None:
                logger.error("No user with username `%s' found.", username)
                return False
            contest = Contest.get_from_id(contest_id, session)
            if contest is None:
                logger.error("No contest with id `%s' found.", contest_id)
                return False
            team = None
            if team_code is not None:
                team = \
                    session.query(Team).filter(Team.code == team_code).first()
                if team is None:
                    logger.error("No team with code `%s' found.", team_code)
                    return False
            if password is not None:
                if is_hashed:
                    password = build_password(password, method)
                else:
                    password = hash_password(password, method)

            participation = Participation(
                user=user,
                contest=contest,
                ip=[ipaddress.ip_network(ip)] if ip is not None else None,
                delay_time=datetime.timedelta(seconds=delay_time),
                extra_time=datetime.timedelta(seconds=extra_time),
                password=password,
                team=team,
                hidden=hidden,
                unrestricted=unrestricted)

            session.add(participation)
            session.commit()
    except IntegrityError:
        logger.error("A participation for this user in this contest "
                     "already exists.")
        return False

    logger.info("Participation added.")
    return True
Пример #12
0
def add_participation(username, contest_id, ip, delay_time, extra_time,
                      password, method, is_hashed, team_code, hidden,
                      unrestricted):
    logger.info("Creating the user's participation in the database.")
    delay_time = delay_time if delay_time is not None else 0
    extra_time = extra_time if extra_time is not None else 0

    if hidden:
        logger.warning("The participation will be hidden")
    if unrestricted:
        logger.warning("The participation will be unrestricted")

    try:
        with SessionGen() as session:
            user = \
                session.query(User).filter(User.username == username).first()
            if user is None:
                logger.error("No user with username `%s' found.", username)
                return False
            contest = Contest.get_from_id(contest_id, session)
            if contest is None:
                logger.error("No contest with id `%s' found.", contest_id)
                return False
            team = None
            if team_code is not None:
                team = \
                    session.query(Team).filter(Team.code == team_code).first()
                if team is None:
                    logger.error("No team with code `%s' found.", team_code)
                    return False
            if password is not None:
                if is_hashed:
                    password = build_password(password, method)
                else:
                    password = hash_password(password, method)

            participation = Participation(
                user=user,
                contest=contest,
                ip=[ipaddress.ip_network(ip)] if ip is not None else None,
                delay_time=datetime.timedelta(seconds=delay_time),
                extra_time=datetime.timedelta(seconds=extra_time),
                password=password,
                team=team,
                hidden=hidden,
                unrestricted=unrestricted)

            session.add(participation)
            session.commit()
    except IntegrityError:
        logger.error("A participation for this user in this contest "
                     "already exists.")
        return False

    logger.info("Participation added.")
    return True
Пример #13
0
def add_user(first_name, last_name, username, password, method, is_hashed,
             email, timezone, preferred_languages, overwrite=False):
    logger.info("Creating the user in the database.")
    pwd_generated = False
    if password is None:
        assert not is_hashed
        password = generate_random_password()
        pwd_generated = True
    if is_hashed:
        stored_password = build_password(password, method)
    else:
        stored_password = hash_password(password, method)

    if preferred_languages is None or preferred_languages == "":
        preferred_languages = "[]"
    else:
        preferred_languages = \
            "[" + ",".join("\"" + lang + "\""
                           for lang in preferred_languages.split(",")) + "]"
    user = User(first_name=first_name,
                last_name=last_name,
                username=username,
                password=stored_password,
                email=email,
                timezone=timezone,
                preferred_languages=preferred_languages)

    with SessionGen() as session:
        if overwrite:
            existing_user = session.query(User) \
                .filter(User.username == username).first()
            if existing_user is not None:
                user = existing_user
                user.first_name = first_name
                user.last_name = last_name
                user.username = username
                if not pwd_generated:
                    user.password = stored_password
                else:
                    pwd_generated = False
                user.email = email or user.email
                user.timezone = timezone or user.timezone
                user.preferred_languages = preferred_languages or \
                    user.preferred_languages
        try:
            session.add(user)
            session.commit()
        except IntegrityError:
            logger.error("A user with the given username already exists.")
            return False

    logger.info("User added%s. "
                "Use AddParticipation to add this user to a contest."
                % (" with password %s" % password if pwd_generated else ""))
    return True
Пример #14
0
 def setUp(self):
     super(TestAuthenticateRequest, self).setUp()
     self.timestamp = make_datetime()
     self.contest = self.add_contest()
     self.user = self.add_user(
         username="******", password=build_password("mypass"))
     self.participation = self.add_participation(
         contest=self.contest, user=self.user)
     _, self.cookie = validate_login(
         self.session, self.contest, self.timestamp, self.user.username,
         "mypass", ipaddress.ip_address("10.0.0.1"))
Пример #15
0
    def _makeparticipation(self, username, cdb, udb, gdb, teamdb):
        user = self.users[username]

        pdb = Participation(user=udb, contest=cdb)

        pdb.password = build_password(user.password)
        pdb.group = gdb

        pdb.ip = user.ip
        pdb.hidden = user.hidden
        pdb.unrestricted = user.unrestricted

        pdb.team = teamdb

        return pdb
Пример #16
0
    def _makeparticipation(self, username, cdb, udb, gdb, teamdb):
        user = self.users[username]

        pdb = Participation(user=udb, contest=cdb)

        pdb.password = build_password(user.password)
        pdb.group = gdb

        pdb.ip = user.ip
        pdb.hidden = user.hidden
        pdb.unofficial = user.unofficial
        pdb.unrestricted = user.unrestricted

        pdb.team = teamdb

        return pdb
Пример #17
0
def generate_passwords(contest_id, exclude_hidden, exclude_unrestricted,
                       output_path):
    logger.info("Updating passwords...")

    with open(output_path, 'w') as io:
        io.write("contest_id,team,fullname,username,password\n")
        with SessionGen() as session:
            if contest_id is not None:
                contest = Contest.get_from_id(contest_id, session)
                objects = session.query(Participation).join(
                    Participation.user).join(Participation.team)
                if exclude_unrestricted:
                    objects = objects.filter(
                        Participation.unrestricted == False)
                if exclude_hidden:
                    objects = objects.filter(Participation.hidden == False)
            else:
                objects = session.query(User)

            for obj in objects:
                password = generate_random_password()
                obj.password = build_password(password, 'plaintext')

                user = obj if isinstance(obj, User) else obj.user
                fullname = "%s %s" % (user.first_name, user.last_name)
                if isinstance(obj, Participation):
                    team = obj.team.code if obj.team is not None else ''
                    logger.info(
                        "Updating participation of user %s (team=%s) on contest id %d",
                        user.username, team, contest.id)
                    io.write(
                        "%d,%s,%s,%s,%s\n" %
                        (contest.id, team, fullname, user.username, password))
                else:
                    logger.info("Updating user %s", user.username)
                    io.write(",,%s,%s,%s\n" %
                             (fullname, user.username, password))

            session.commit()

    logger.info("Done.")
    return True
Пример #18
0
    def get_user(self):
        """See docstring in class Loader.

        """

        username = os.path.basename(self.path)
        userdata = None

        # This is not standard Polygon feature, but useful for CMS users
        # we assume contestants.txt contains one line for each user:
        #
        # username;password;first_name;last_name;hidden
        #
        # For example:
        #
        # contestant1;123;Cont;Estant;0
        # jury;1234;Ju;Ry;1

        users_path = os.path.join(os.path.dirname(self.path),
                                  'contestants.txt')
        if os.path.exists(users_path):
            with io.open(users_path, "rt", encoding="utf-8") as users_file:
                for user in users_file.readlines():
                    user = user.strip().split(';')
                    name = user[0].strip()
                    if name == username:
                        userdata = [x.strip() for x in user]

        if userdata is not None:
            logger.info("Loading parameters for user %s.", username)
            args = {}
            args['username'] = userdata[0]
            args['password'] = build_password(userdata[1])
            args['first_name'] = userdata[2]
            args['last_name'] = userdata[3]
            args['hidden'] = (len(userdata) > 4 and userdata[4] == '1')
            logger.info("User parameters loaded.")
            return User(**args)
        else:
            logger.critical("User %s not found in contestants.txt file.",
                            username)
            return None
Пример #19
0
    def get_user(self):
        """See docstring in class Loader.

        """

        username = os.path.basename(self.path)
        userdata = None

        # This is not standard Polygon feature, but useful for CMS users
        # we assume contestants.txt contains one line for each user:
        #
        # username;password;first_name;last_name;hidden
        #
        # For example:
        #
        # contestant1;123;Cont;Estant;0
        # jury;1234;Ju;Ry;1

        users_path = os.path.join(
            os.path.dirname(self.path), 'contestants.txt')
        if os.path.exists(users_path):
            with io.open(users_path, "rt", encoding="utf-8") as users_file:
                for user in users_file.readlines():
                    user = user.strip().split(';')
                    name = user[0].strip()
                    if name == username:
                        userdata = [x.strip() for x in user]

        if userdata is not None:
            logger.info("Loading parameters for user %s.", username)
            args = {}
            args['username'] = userdata[0]
            args['password'] = build_password(userdata[1])
            args['first_name'] = userdata[2]
            args['last_name'] = userdata[3]
            args['hidden'] = (len(userdata) > 4 and userdata[4] == '1')
            logger.info("User parameters loaded.")
            return User(**args)
        else:
            logger.critical(
                "User %s not found in contestants.txt file.", username)
            return None
Пример #20
0
def add_user(first_name, last_name, username, password, method, is_hashed,
             email, timezone, preferred_languages):
    logger.info("Creating the user in the database.")
    pwd_generated = False
    if password is None:
        assert not is_hashed
        password = generate_random_password()
        pwd_generated = True
    if is_hashed:
        stored_password = build_password(password, method)
    else:
        stored_password = hash_password(password, method)

    if preferred_languages is None:
        preferred_languages = []
    else:
        preferred_languages = list(
            lang.strip() for lang in preferred_languages.split(",") if
            lang.strip())
    user = User(first_name=first_name,
                last_name=last_name,
                username=username,
                password=stored_password,
                email=email,
                timezone=timezone,
                preferred_languages=preferred_languages)
    try:
        with SessionGen() as session:
            session.add(user)
            session.commit()
    except IntegrityError:
        logger.error("A user with the given username already exists.")
        return False

    logger.info("User added%s. "
                "Use AddParticipation to add this user to a contest."
                % (" with password %s" % password if pwd_generated else ""))
    return True
Пример #21
0
def add_user(first_name, last_name, username, password, method, is_hashed,
             email, timezone, preferred_languages):
    logger.info("Creating the user in the database.")
    pwd_generated = False
    if password is None:
        assert not is_hashed
        password = generate_random_password()
        pwd_generated = True
    if is_hashed:
        stored_password = build_password(password, method)
    else:
        stored_password = hash_password(password, method)

    if preferred_languages is None:
        preferred_languages = []
    else:
        preferred_languages = list(lang.strip()
                                   for lang in preferred_languages.split(",")
                                   if lang.strip())
    user = User(first_name=first_name,
                last_name=last_name,
                username=username,
                password=stored_password,
                email=email,
                timezone=timezone,
                preferred_languages=preferred_languages)
    try:
        with SessionGen() as session:
            session.add(user)
            session.commit()
    except IntegrityError:
        logger.error("A user with the given username already exists.")
        return False

    logger.info("User added%s. "
                "Use AddParticipation to add this user to a contest." %
                (" with password %s" % password if pwd_generated else ""))
    return True
Пример #22
0
    def _makeuser(self, username):
        """
        Return a User object for the specified user which can be saved
        to the database.

        username (unicode): the name of the user to generate

        return (User,Participation): database object for the user

        """
        user = self.users[username]

        # The user should never actually use this password, because we set
        # different passwords for each participation.
        udb = User(username=user.username,
                   first_name=user.firstname,
                   last_name=user.lastname,
                   password=build_password(user.password))

        udb.timezone = user.timezone
        udb.preferred_languages = user.primary_statements

        return udb
Пример #23
0
    def _makeuser(self, username):
        """
        Return a User object for the specified user which can be saved
        to the database.

        username (unicode): the name of the user to generate

        return (User,Participation): database object for the user

        """
        user = self.users[username]

        # The user should never actually use this password, because we set
        # different passwords for each participation.
        udb = User(username=user.username,
                   first_name=user.firstname,
                   last_name=user.lastname,
                   password=build_password(user.password))

        udb.timezone = user.timezone
        udb.preferred_languages = user.primary_statements

        return udb
Пример #24
0
    def get(self):
        self.create_client()

        # When we have a code, request id token
        if self.get_argument('code', False):
            response = self.request.query
            response = self.client.parse_response(
                AuthorizationResponse, info=response,
                sformat="urlencoded")

            # The query is checked against the state in the cookie
            code = response['code'] if 'code' in response else None
            state = response['state'] if 'state' in response else None
            cookie = self.get_secure_cookie(self.contest.name + "_auth")
            if code and cookie and json.loads(cookie)[0] == state:
                args = {
                    "code": code,
                    "token_endpoint": self.op_info["token_endpoint"],
                    "redirect_uri": self.redirect_uri,
                }
                # Request an access token to the authentication server
                resp = yield self.get_access_token(
                    state=state,
                    request_args=args,
                    authn_method="client_secret_basic"
                )
            else:
                self.redirect_login_error()

            self.clear_cookie(self.contest.name + "_auth")

            # The token is checked against the nonce in the cookie
            id_token = resp["id_token"]
            if id_token["nonce"] != json.loads(cookie)[1]:
                self.redirect_login_error()

            first_name = (
                id_token["given_name"]
                if "given_name" in id_token
                else "")
            last_name = (
                id_token["family_name"]
                if "family_name" in id_token
                else "")
            email = (
                id_token["email"]
                if "email" in id_token
                else "")
            username = id_token["sub"]

            # Check if the user already exists
            user = self.sql_session.query(User)\
                .filter(User.username == username).first()

            # Create the user if it doesn't exist yet
            if user is None:
                user = User(
                    first_name, last_name, username, email=email,
                    password=build_password(generate_random_password()))
                self.sql_session.add(user)
                self.sql_session.commit()
            if not [p for p in user.participations
                    if p.contest_id == self.contest.id]:
                participation = Participation(
                    contest=self.contest, user=user)
                self.sql_session.add(participation)
                self.sql_session.commit()

            self.try_user_login(user)

        # Request a code
        else:
            state = rndstr()
            nonce = rndstr()
            self.set_secure_cookie(
                self.contest.name + "_auth",
                json.dumps([state, nonce]),
                expires_days=None)
            claims_request = ClaimsRequest(
                id_token=Claims(
                    sub={"essential": True}
                ),
                userinfo=Claims(
                    given_name={"essential": True},
                    family_name={"essential": True},
                    preferred_username={"essential": True},
                )
            )
            args = {
                "client_id": self.client.client_id,
                "response_type": "code",
                "scope": ["openid", "offline_access"],
                "nonce": nonce,
                "redirect_uri": self.redirect_uri,
                "state": state,
                "claims": claims_request,
            }
            next_page = self.get_argument('next', None)
            if next_page:
                args["next"] = next_page

            auth_req = (self.client.construct_AuthorizationRequest
                        (request_args=args))
            login_url = auth_req.request(
                self.op_info["authorization_endpoint"])
            self.redirect(login_url)
Пример #25
0
    def get_user(self):
        """See docstring in class Loader.

        """

        username = os.path.basename(self.path)
        userdata = None

        # Shaker user format (CSV-based):
        #
        # This is not standard Polygon feature, but useful for CMS users
        # we assume contestants.txt contains one line for each user, and
        # a header line, as follow:
        #
        # ...,Prénom,Nom,Pseudo,Mot de passe,...
        # (additionnal columns are accepted, and order isn't important)
        #

        users_path = os.path.join(os.path.dirname(self.path),
                                  'contestants.csv')
        if not os.path.exists(users_path):
            users_path = os.path.join(os.path.dirname(self.path),
                                      '../contestants.csv')
        if not os.path.exists(users_path):
            logger.critical("contestants.csv not found!")
            exit(1)
            return None

        try:
            headers = dict()
            if os.path.exists(users_path):
                with io.open(users_path, "rt", encoding="utf-8") as users_file:
                    reader = unicode_csv_reader(users_file)

                    # Headers
                    headers_list = next(reader)
                    headers = dict(zip(headers_list, range(len(headers_list))))

                    # Content
                    for user in reader:
                        if len(user) == 0:
                            continue
                        name = user[headers['Pseudo']].strip()
                        if name == username:
                            userdata = [x.strip() for x in user]
                            break

            def get_param(param, default=None):
                try:
                    return userdata[headers[param]]
                except KeyError as _:
                    if default is None:
                        raise _
                    return default

            if userdata is not None:
                logger.info("Loading parameters for user %s.", username)
                args = {
                    'username': get_param('Pseudo'),
                    'password': get_param('Mot de passe', ''),
                    'first_name': get_param('Prénom', ''),
                    'last_name': get_param('Nom', '')
                }
                # args['hidden'] = get_param('hidden', False) == '1'

                # Generate a password if none is defined
                if len(args['password']) == 0:
                    args['password'] = random_password()

                # Build an auth string from the password
                args['password'] = build_password(args['password'])

                logger.info("User parameters loaded.")
                return User(**args)
            else:
                logger.critical("User %s not found in contestants.csv file.",
                                username)
                exit(1)
                return None
        except KeyError as e:
            logger.critical(
                "contestants.csv is ill-formed: column %s not found!", e)
            exit(1)
            return None
Пример #26
0
 def test_default_plaintext(self):
     self.assertEqual(build_password("plain"), "plaintext:plain")
Пример #27
0
    def get_contest(self):
        """See docstring in class Loader.

        """

        name = os.path.split(self.path)[1]

        logger.info("Loading parameters for contest %s.", name)

        args = {}

        tree = ET.parse(os.path.join(self.path, "contest.xml"))
        root = tree.getroot()

        args['name'] = name

        # TODO: find proper way to choose contest primary language.

        self.primary_language = root.find('names') \
            .find('name').attrib['language']

        # All available contest languages are allowed to be used.

        self.languages = []
        for alternative_name in root.find('names'):
            self.languages.append(alternative_name.attrib['language'])

        logger.info("Contest languages are %s %s",
                    self.primary_language, str(self.languages))

        args['description'] = root.find('names') \
            .find("name[@language='%s']" % self.primary_language) \
            .attrib['value']

        logger.info("Contest description is %s", args['description'])

        # For now Polygon doesn't support custom contest-wide files,
        # so we need to hardcode some contest settings.

        args['start'] = datetime(1970, 1, 1)
        args['stop'] = datetime(1970, 1, 1)

        # Uncomment the following to set specific values for these
        # options.

        # args['max_submission_number'] = 100
        # args['max_user_test_number'] = 100
        # args['min_submission_interval'] = make_timedelta(60)
        # args['min_user_test_interval'] = make_timedelta(60)
        # args['max_user_test_number'] = 10
        # args['min_user_test_interval'] = make_timedelta(60)

        # args['token_mode'] = 'infinite'
        # args['token_max_number'] = 100
        # args['token_min_interval'] = make_timedelta(60)
        # args['token_gen_initial'] = 1
        # args['token_gen_number'] = 1
        # args['token_gen_interval'] = make_timedelta(1800)
        # args['token_gen_max'] = 2

        logger.info("Contest parameters loaded.")

        tasks = []
        for problem in root.find('problems'):
            tasks.append(os.path.basename(problem.attrib['url']))

        participations = []

        # This is not standard Polygon feature, but useful for CMS users
        # we assume contestants.txt contains one line for each user:
        #
        # username;password;first_name;last_name;hidden
        #
        # For example:
        #
        # contestant1;123;Cont;Estant;0
        # jury;1234;Ju;Ry;1

        users_path = os.path.join(self.path, 'contestants.txt')
        if os.path.exists(users_path):
            with io.open(users_path, "rt", encoding="utf-8") as users_file:
                for user in users_file.readlines():
                    user = user.strip()
                    user = user.split(';')
                    participations.append({
                        "username": user[0].strip(),
                        "password": build_password(user[1].strip()),
                        "hidden": user[4].strip()
                        # "ip" is not passed
                    })

        return Contest(**args), tasks, participations
Пример #28
0
 def test_non_ascii(self):
     self.assertEqual(build_password("p你好", "m你好"), "m你好:p你好")
Пример #29
0
    def test_participation_specific_password(self):
        self.participation.password = build_password("myotherpass")

        self.assertFailure("myuser", "mypass", "127.0.0.1")
        self.assertSuccess("myuser", "myotherpass", "127.0.0.1")
Пример #30
0
 def test_success(self):
     self.assertEqual(build_password("pwd", "method"), "method:pwd")
Пример #31
0
 def test_non_ascii(self):
     self.assertEqual(build_password("p你好", "m你好"), "m你好:p你好")
Пример #32
0
    def test_participation_specific_password(self):
        self.participation.password = build_password("myotherpass")

        self.assertFailure("myuser", "mypass", "127.0.0.1")
        self.assertSuccess("myuser", "myotherpass", "127.0.0.1")
Пример #33
0
    def get_contest(self):
        """See docstring in class ContestLoader."""
        if not os.path.exists(os.path.join(self.path, "contest.yaml")):
            logger.critical("File missing: \"contest.yaml\"")
            return None

        conf = load_yaml_from_path(os.path.join(self.path, "contest.yaml"))

        # Here we update the time of the last import
        touch(os.path.join(self.path, ".itime_contest"))
        # If this file is not deleted, then the import failed
        touch(os.path.join(self.path, ".import_error_contest"))

        args = {}

        # Contest Information
        load(conf, args, ["name", "nome_breve"])
        logger.info("Loading parameters for contest %s.", args["name"])

        load(conf, args, ["description", "nome"])
        load(conf, args, "allowed_localizations")
        load(conf, args, ["languages", "allowed_programming_languages"])
        load(conf, args, "submissions_download_allowed")
        load(conf, args, "allow_questions")
        load(conf, args, "allow_user_tests")
        load(conf, args, ["score_precision", "score_decimal_places"])

        # Logging in
        load(conf, args, "block_hidden_participations")
        load(conf, args, "allow_password_authentication")
        load(conf, args, "allow_registration")
        load(conf, args, ["ip_restriction","ip_based_login_restriction"])
        load(conf, args, ["ip_autologin","ip_based_autologin"])

        # Tokens parameters
        # Use the new token settings format if detected.
        if "token_mode" in conf:
            load(conf, args, "token_mode")
            load(conf, args, ["token_max_number","maximum_number_of_tokens"])
            load(conf, args, ["token_min_interval","minimum_interval_between_tokens"], conv=make_timedelta)
            load(conf, args, ["token_gen_initial","initial_number_of_tokens"])
            load(conf, args, ["token_gen_number","token_generation_number"])
            load(conf, args, ["token_gen_interval","token_generation_period"], conv=make_timedelta)
            load(conf, args, ["token_gen_max","maximum_accumulated_tokens"])
        # Otherwise fall back on the old one.
        else:
            logger.warning(
                "contest.yaml uses a deprecated format for token settings "
                "which will soon stop being supported, you're advised to "
                "update it.")
            # Determine the mode.
            if conf.get("token_initial", None) is None:
                args["token_mode"] = TOKEN_MODE_DISABLED
            elif conf.get("token_gen_number", 0) > 0 and \
                    conf.get("token_gen_time", 0) == 0:
                args["token_mode"] = TOKEN_MODE_INFINITE
            else:
                args["token_mode"] = TOKEN_MODE_FINITE
            # Set the old default values.
            args["token_gen_initial"] = 0
            args["token_gen_number"] = 0
            args["token_gen_interval"] = timedelta()
            # Copy the parameters to their new names.
            load(conf, args, "token_total", "token_max_number")
            load(conf, args, "token_min_interval", conv=make_timedelta)
            load(conf, args, "token_initial", "token_gen_initial")
            load(conf, args, "token_gen_number")
            load(conf, args, "token_gen_time", "token_gen_interval",
                 conv=make_timedelta)
            load(conf, args, "token_max", "token_gen_max")
            # Remove some corner cases.
            if args["token_gen_initial"] is None:
                args["token_gen_initial"] = 0
            if args["token_gen_interval"].total_seconds() == 0:
                args["token_gen_interval"] = timedelta(minutes=1)

        # Times
        load(conf, args, ["start", "start_time", "start_time_in_utc", "inizio"], conv=make_utc_datetime)
        load(conf, args, ["stop", "end_time", "end_time_in_utc", "fine"], conv=make_utc_datetime)
        load(conf, args, ["per_user_time","length_of_the_contest"], conv=make_timedelta)
        load(conf, args, ["timezone"])

        # Limits
        load(conf, args, ["max_submission_number","maximum_number_of_submissions"])
        load(conf, args, ["max_user_test_number","maximum_number_of_user_tests"])
        load(conf, args, ["min_submission_interval","minimum_interval_between_submissions"], conv=make_timedelta)
        load(conf, args, ["min_user_test_interval","minimum_interval_between_user_tests"], conv=make_timedelta)

        # Analysis
        load(conf, args, ["analysis_enabled", "enabled"])
        load(conf, args, ["analysis_start", "analysis_mode_start_time_in_utc"], conv=make_utc_datetime)
        load(conf, args, ["analysis_stop", "analysis_mode_end_time_in_utc"], conv=make_utc_datetime)


        taskpaths = load(conf, None, ["tasks", "problemi"])
        
        # read true name of tasks
        tasks = []
        self.task_path_map = {}
        for taskpath in taskpaths:
            name = taskpath
            try:
                conf = load_yaml_from_path(os.path.join(self.path, taskpath, "task.yaml"))
                name = conf.get("name", taskpath)
            except:
                try:
                    conf = load_yaml_from_path(os.path.join(self.path, taskpath + ".yaml"))
                    name = conf.get("name", taskpath)
                except:
                    pass

            tasks.append(name)
            self.task_path_map[name] = os.path.join(self.path, taskpath)

        participations = load(conf, None, ["users", "participations"])
        if participations:
            for p in participations:
                p["password"] = build_password(p["password"])

        # Import was successful
        os.remove(os.path.join(self.path, ".import_error_contest"))

        logger.info("Contest parameters loaded.")

        return Contest(**args), tasks, participations
Пример #34
0
    def get_user(self):
        """See docstring in class Loader.
        """

        username = os.path.basename(self.path)
        userdata = None

        # Shaker user format (CSV-based):
        #
        # This is not standard Polygon feature, but useful for CMS users
        # we assume contestants.txt contains one line for each user, and
        # a header line, as follow:
        #
        # ...,Prénom,Nom,Pseudo,Mot de passe,...
        # (additionnal columns are accepted, and order isn't important)
        #

        users_path = os.path.join(
            os.path.dirname(self.path), 'contestants.csv')
        if not os.path.exists(users_path):
            users_path = os.path.join(
                os.path.dirname(self.path), '../contestants.csv')
        if not os.path.exists(users_path):
            logger.critical("contestants.csv not found!")
            exit(1)
            return None

        try:
            headers = dict()
            if os.path.exists(users_path):
                with io.open(users_path, "rt", encoding="utf-8") as users_file:
                    reader = unicode_csv_reader(users_file)

                    # Headers
                    headers_list = next(reader)
                    headers = dict(zip(headers_list, range(len(headers_list))))

                    # Content
                    for user in reader:
                        if len(user) == 0:
                            continue
                        name = user[headers['Pseudo']].strip()
                        if name == username:
                            userdata = [x.strip() for x in user]
                            break

            def get_param(param, default=None):
                try:
                    return userdata[headers[param]]
                except KeyError as _:
                    if default is None:
                        raise _
                    return default

            if userdata is not None:
                logger.info("Loading parameters for user %s.", username)
                args = {
                    'username': get_param('Pseudo'),
                    'password': get_param('Mot de passe', ''),
                    'first_name': get_param('Prénom', ''),
                    'last_name': get_param('Nom', '')
                }
                # args['hidden'] = get_param('hidden', False) == '1'

                # Generate a password if none is defined
                if len(args['password']) == 0:
                    args['password'] = random_password()

                # Build an auth string from the password
                args['password'] = build_password(args['password'])

                logger.info("User parameters loaded.")
                return User(**args)
            else:
                logger.critical(
                    "User %s not found in contestants.csv file.", username)
                exit(1)
                return None
        except KeyError as e:
            logger.critical(
                "contestants.csv is ill-formed: column %s not found!", e)
            exit(1)
            return None
Пример #35
0
 def test_default_plaintext(self):
     self.assertEqual(build_password("plain"), "plaintext:plain")
Пример #36
0
    def get_contest(self):
        """See docstring in class ContestLoader."""
        if not os.path.exists(os.path.join(self.path, "contest.yaml")):
            logger.critical("File missing: \"contest.yaml\"")
            return None

        conf = load_yaml_from_path(os.path.join(self.path, "contest.yaml"))

        # Here we update the time of the last import
        touch(os.path.join(self.path, ".itime_contest"))
        # If this file is not deleted, then the import failed
        touch(os.path.join(self.path, ".import_error_contest"))

        args = {}

        load(conf, args, ["name", "nome_breve"])
        load(conf, args, ["description", "nome"])

        logger.info("Loading parameters for contest %s.", args["name"])

        # Use the new token settings format if detected.
        if "token_mode" in conf:
            load(conf, args, "token_mode")
            load(conf, args, "token_max_number")
            load(conf, args, "token_min_interval", conv=make_timedelta)
            load(conf, args, "token_gen_initial")
            load(conf, args, "token_gen_number")
            load(conf, args, "token_gen_interval", conv=make_timedelta)
            load(conf, args, "token_gen_max")
        # Otherwise fall back on the old one.
        else:
            logger.warning(
                "contest.yaml uses a deprecated format for token settings "
                "which will soon stop being supported, you're advised to "
                "update it.")
            # Determine the mode.
            if conf.get("token_initial", None) is None:
                args["token_mode"] = TOKEN_MODE_DISABLED
            elif conf.get("token_gen_number", 0) > 0 and \
                    conf.get("token_gen_time", 0) == 0:
                args["token_mode"] = TOKEN_MODE_INFINITE
            else:
                args["token_mode"] = TOKEN_MODE_FINITE
            # Set the old default values.
            args["token_gen_initial"] = 0
            args["token_gen_number"] = 0
            args["token_gen_interval"] = timedelta()
            # Copy the parameters to their new names.
            load(conf, args, "token_total", "token_max_number")
            load(conf, args, "token_min_interval", conv=make_timedelta)
            load(conf, args, "token_initial", "token_gen_initial")
            load(conf, args, "token_gen_number")
            load(conf, args, "token_gen_time", "token_gen_interval",
                 conv=make_timedelta)
            load(conf, args, "token_max", "token_gen_max")
            # Remove some corner cases.
            if args["token_gen_initial"] is None:
                args["token_gen_initial"] = 0
            if args["token_gen_interval"].total_seconds() == 0:
                args["token_gen_interval"] = timedelta(minutes=1)

        load(conf, args, ["start", "inizio"], conv=make_datetime)
        load(conf, args, ["stop", "fine"], conv=make_datetime)
        load(conf, args, ["per_user_time"], conv=make_timedelta)
        load(conf, args, ["timezone"])

        load(conf, args, "max_submission_number")
        load(conf, args, "max_user_test_number")
        load(conf, args, "min_submission_interval", conv=make_timedelta)
        load(conf, args, "min_user_test_interval", conv=make_timedelta)

        tasks = load(conf, None, ["tasks", "problemi"])
        participations = load(conf, None, ["users", "utenti"])
        for p in participations:
            p["password"] = build_password(p["password"])

        # Import was successful
        os.remove(os.path.join(self.path, ".import_error_contest"))

        logger.info("Contest parameters loaded.")

        return Contest(**args), tasks, participations
Пример #37
0
def load_participations(path):
    logger.info("Loading...")
    with open(path, 'r') as io:
        data = json.load(io)

    participations = data['participations']
    with SessionGen() as session:
        for entry in participations:
            logger.info('Loading: %s' % (entry))
            contest = Contest.get_from_id(entry['contest_id'], session)
            if contest is None:
                logger.error("  Contest ID %d not found" %
                             (entry['contest_id']))
                session.rollback()
                return False

            userdata = entry['user']
            user = session.query(User).filter(
                User.username == userdata['username']).first()
            if user is None:
                user = User(username=userdata['username'],
                            first_name=userdata['first_name'],
                            last_name=userdata['last_name'],
                            password=build_password(
                                generate_random_password()))
                logger.info('  Creating new user: %s' % (user.username))
                session.add(user)
            else:
                logger.info('  Using existing user: %s (id=%d)' %
                            (user.username, user.id))

            if 'plaintext_password' in userdata:
                logger.info('  * password')
                user.password = build_password(userdata['plaintext_password'],
                                               'plaintext')

            if 'first_name' in userdata:
                logger.info('  * first_name: %s' % (userdata['first_name']))
                user.first_name = userdata['first_name']
            if 'last_name' in userdata:
                logger.info('  * last_name: %s' % (userdata['last_name']))
                user.last_name = userdata['last_name']

            participation = session.query(Participation).join(
                Participation.user).filter(
                    Participation.contest == contest).filter(
                        User.username == user.username).first()
            if participation is None:
                participation = Participation(user=user, contest=contest)
                logger.info(
                    '  Creating new participation for contest_id=%d user=%s' %
                    (contest.id, user.username))
                session.add(participation)
            else:
                logger.info(
                    '  Updating participation: id=%d contest_id=%d user=%s' %
                    (participation.id, participation.contest_id,
                     participation.user.username))

            if 'plaintext_password' in entry:
                logger.info('  * plaintext_password')
                participation.password = build_password(
                    entry['plaintext_password'], 'plaintext')
            if 'ip' in entry:
                logger.info('  * ip: %s' % (entry['ip']))
                participation.ip = [ipaddress.ip_network(entry['ip'])]
            if 'delay_time' in entry:
                logger.info('  * delay_time: %d' % (entry['delay_time']))
                participation.delay_time = datetime.timedelta(
                    seconds=entry['delay_time'])
            if 'extra_time' in entry:
                logger.info('  * extra_time: %d' % (entry['extra_time']))
                participation.extra_time = datetime.timedelta(
                    seconds=entry['extra_time'])
            if 'hidden' in entry:
                logger.info('  * hidden: %s' % (entry['hidden']))
                participation.hidden = entry['hidden']
            if 'unrestricted' in entry:
                logger.info('  * unrestricted: %s' % (entry['unrestricted']))
                participation.unrestricted = entry['unrestricted']

            if 'team' in userdata:
                team = session.query(Team).filter(
                    Team.code == userdata['team']['code']).first()
                if team is None:
                    team = Team(code=userdata['team']['code'],
                                name=userdata['team']['name'])
                    logger.info('  Creating new team: %s' % (team.code))
                    session.add(team)
                else:
                    logger.info('  Using existing team: %s' % (team.code))
                if 'name' in userdata['team']:
                    logger.info('  * name: %s' % (userdata['team']['name']))
                    team.name = userdata['team']['name']
                participation.team = team

        session.commit()

    logger.info("Done.")
    return True
Пример #38
0
    def get_contest(self):
        """See docstring in class ContestLoader."""
        if not os.path.exists(os.path.join(self.path, "contest.yaml")):
            logger.critical("File missing: \"contest.yaml\"")
            return None

        conf = yaml.safe_load(
            io.open(os.path.join(self.path, "contest.yaml"),
                    "rt", encoding="utf-8"))

        # Here we update the time of the last import
        touch(os.path.join(self.path, ".itime_contest"))
        # If this file is not deleted, then the import failed
        touch(os.path.join(self.path, ".import_error_contest"))

        args = {}

        load(conf, args, ["name", "nome_breve"])
        load(conf, args, ["description", "nome"])

        logger.info("Loading parameters for contest %s.", args["name"])

        # Use the new token settings format if detected.
        if "token_mode" in conf:
            load(conf, args, "token_mode")
            load(conf, args, "token_max_number")
            load(conf, args, "token_min_interval", conv=make_timedelta)
            load(conf, args, "token_gen_initial")
            load(conf, args, "token_gen_number")
            load(conf, args, "token_gen_interval", conv=make_timedelta)
            load(conf, args, "token_gen_max")
        # Otherwise fall back on the old one.
        else:
            logger.warning(
                "contest.yaml uses a deprecated format for token settings "
                "which will soon stop being supported, you're advised to "
                "update it.")
            # Determine the mode.
            if conf.get("token_initial", None) is None:
                args["token_mode"] = TOKEN_MODE_DISABLED
            elif conf.get("token_gen_number", 0) > 0 and \
                    conf.get("token_gen_time", 0) == 0:
                args["token_mode"] = TOKEN_MODE_INFINITE
            else:
                args["token_mode"] = TOKEN_MODE_FINITE
            # Set the old default values.
            args["token_gen_initial"] = 0
            args["token_gen_number"] = 0
            args["token_gen_interval"] = timedelta()
            # Copy the parameters to their new names.
            load(conf, args, "token_total", "token_max_number")
            load(conf, args, "token_min_interval", conv=make_timedelta)
            load(conf, args, "token_initial", "token_gen_initial")
            load(conf, args, "token_gen_number")
            load(conf, args, "token_gen_time", "token_gen_interval",
                 conv=make_timedelta)
            load(conf, args, "token_max", "token_gen_max")
            # Remove some corner cases.
            if args["token_gen_initial"] is None:
                args["token_gen_initial"] = 0
            if args["token_gen_interval"].total_seconds() == 0:
                args["token_gen_interval"] = timedelta(minutes=1)

        load(conf, args, ["start", "inizio"], conv=make_datetime)
        load(conf, args, ["stop", "fine"], conv=make_datetime)
        load(conf, args, ["per_user_time"], conv=make_timedelta)

        load(conf, args, "max_submission_number")
        load(conf, args, "max_user_test_number")
        load(conf, args, "min_submission_interval", conv=make_timedelta)
        load(conf, args, "min_user_test_interval", conv=make_timedelta)

        tasks = load(conf, None, ["tasks", "problemi"])
        participations = load(conf, None, ["users", "utenti"])
        for p in participations:
            p["password"] = build_password(p["password"])

        # Import was successful
        os.remove(os.path.join(self.path, ".import_error_contest"))

        logger.info("Contest parameters loaded.")

        return Contest(**args), tasks, participations
Пример #39
0
    def get_contest(self):
        """See docstring in class ContestLoader."""
        if not os.path.exists(os.path.join(self.path, "contest.yaml")):
            logger.critical("File missing: \"contest.yaml\"")
            return None

        conf = yaml.safe_load(
            io.open(os.path.join(self.path, "contest.yaml"),
                    "rt",
                    encoding="utf-8"))

        # Here we update the time of the last import
        touch(os.path.join(self.path, ".itime_contest"))
        # If this file is not deleted, then the import failed
        touch(os.path.join(self.path, ".import_error_contest"))

        args = {}

        load(conf, args, ["name", "nome_breve"])
        load(conf, args, ["description", "nome"])
        load(conf, args, "presentation")
        load(conf, args, "path_to_logo")
        load(conf, args, "timezone")

        pr_temp = load(conf, None, "presentation_translations")
        if pr_temp:
            presentation_translations = {
                language: Presentation(language, pr_temp[language])
                for language in pr_temp
            }
        else:
            presentation_translations = {}
        args["presentation_translations"] = presentation_translations

        # Authentication methods
        oic_info = load(conf, None, "oic_info")
        if oic_info:
            args["auth_type"] = "OpenIDConnect"
            with open(os.path.join(self.path, oic_info), "r") as f:
                args["openidconnect_info"] = f.read()

        # To limit the interface to a few languages
        load(conf, args, "allowed_localizations")

        # Allowed programming languages
        plang = load(conf, None, "allowed_languages")
        if plang:
            args["languages"] = plang

        logger.info("Loading parameters for contest %s.", args["name"])

        # Use the new token settings format if detected.
        if "token_mode" in conf:
            load(conf, args, "token_mode")
            load(conf, args, "token_max_number")
            load(conf, args, "token_min_interval", conv=make_timedelta)
            load(conf, args, "token_gen_initial")
            load(conf, args, "token_gen_number")
            load(conf, args, "token_gen_interval", conv=make_timedelta)
            load(conf, args, "token_gen_max")
        # Otherwise fall back on the old one.
        else:
            logger.warning(
                "contest.yaml uses a deprecated format for token settings "
                "which will soon stop being supported, you're advised to "
                "update it.")
            # Determine the mode.
            if conf.get("token_initial", None) is None:
                args["token_mode"] = TOKEN_MODE_DISABLED
            elif conf.get("token_gen_number", 0) > 0 and \
                    conf.get("token_gen_time", 0) == 0:
                args["token_mode"] = TOKEN_MODE_INFINITE
            else:
                args["token_mode"] = TOKEN_MODE_FINITE
            # Set the old default values.
            args["token_gen_initial"] = 0
            args["token_gen_number"] = 0
            args["token_gen_interval"] = timedelta()
            # Copy the parameters to their new names.
            load(conf, args, "token_total", "token_max_number")
            load(conf, args, "token_min_interval", conv=make_timedelta)
            load(conf, args, "token_initial", "token_gen_initial")
            load(conf, args, "token_gen_number")
            load(conf,
                 args,
                 "token_gen_time",
                 "token_gen_interval",
                 conv=make_timedelta)
            load(conf, args, "token_max", "token_gen_max")
            # Remove some corner cases.
            if args["token_gen_initial"] is None:
                args["token_gen_initial"] = 0
            if args["token_gen_interval"].total_seconds() == 0:
                args["token_gen_interval"] = timedelta(minutes=1)

        load(conf, args, ["start", "inizio"], conv=make_datetime)
        load(conf, args, ["stop", "fine"], conv=make_datetime)
        load(conf, args, ["per_user_time"], conv=make_timedelta)

        load(conf, args, "max_submission_number")
        load(conf, args, "max_user_test_number")
        load(conf, args, "min_submission_interval", conv=make_timedelta)
        load(conf, args, "min_user_test_interval", conv=make_timedelta)

        tasks = load(conf, None, ["tasks", "problemi"])
        participations = load(conf, None, ["users", "utenti"])
        if participations is not None:
            for p in participations:
                p["password"] = build_password(p["password"])

        # Import was successful
        os.remove(os.path.join(self.path, ".import_error_contest"))

        logger.info("Contest parameters loaded.")

        return Contest(**args), tasks, participations
Пример #40
0
    def get_contest(self):
        """See docstring in class Loader.

        """

        name = os.path.split(self.path)[1]

        logger.info("Loading parameters for contest %s.", name)

        args = {}

        tree = ET.parse(os.path.join(self.path, "contest.xml"))
        root = tree.getroot()

        args['name'] = name

        # TODO: find proper way to choose contest primary language.

        self.primary_language = root.find('names') \
            .find('name').attrib['language']

        # All available contest languages are allowed to be used.

        self.languages = []
        for alternative_name in root.find('names'):
            self.languages.append(alternative_name.attrib['language'])

        logger.info("Contest languages are %s %s", self.primary_language,
                    str(self.languages))

        args['description'] = root.find('names') \
            .find("name[@language='%s']" % self.primary_language) \
            .attrib['value']

        logger.info("Contest description is %s", args['description'])

        # For now Polygon doesn't support custom contest-wide files,
        # so we need to hardcode some contest settings.

        args['start'] = datetime(1970, 1, 1)
        args['stop'] = datetime(1970, 1, 1)

        # Uncomment the following to set specific values for these
        # options.

        # args['max_submission_number'] = 100
        # args['max_user_test_number'] = 100
        # args['min_submission_interval'] = make_timedelta(60)
        # args['min_user_test_interval'] = make_timedelta(60)
        # args['max_user_test_number'] = 10
        # args['min_user_test_interval'] = make_timedelta(60)

        # args['token_mode'] = 'infinite'
        # args['token_max_number'] = 100
        # args['token_min_interval'] = make_timedelta(60)
        # args['token_gen_initial'] = 1
        # args['token_gen_number'] = 1
        # args['token_gen_interval'] = make_timedelta(1800)
        # args['token_gen_max'] = 2

        logger.info("Contest parameters loaded.")

        tasks = []
        for problem in root.find('problems'):
            tasks.append(os.path.basename(problem.attrib['url']))

        participations = []

        # This is not standard Polygon feature, but useful for CMS users
        # we assume contestants.txt contains one line for each user:
        #
        # username;password;first_name;last_name;hidden
        #
        # For example:
        #
        # contestant1;123;Cont;Estant;0
        # jury;1234;Ju;Ry;1

        users_path = os.path.join(self.path, 'contestants.txt')
        if os.path.exists(users_path):
            with io.open(users_path, "rt", encoding="utf-8") as users_file:
                for user in users_file.readlines():
                    user = user.strip()
                    user = user.split(';')
                    participations.append({
                        "username":
                        user[0].strip(),
                        "password":
                        build_password(user[1].strip()),
                        "hidden":
                        user[4].strip()
                        # "ip" is not passed
                    })

        return Contest(**args), tasks, participations
Пример #41
0
 def test_success(self):
     self.assertEqual(build_password("pwd", "method"), "method:pwd")