Ejemplo n.º 1
0
 def assertParticipationInDb(self,
                             user_id,
                             contest_id,
                             password,
                             delay_time=0,
                             extra_time=0,
                             hidden=False,
                             unrestricted=False,
                             ip=None,
                             team_code=None):
     """Assert that the participation with the given data is in the DB."""
     db_participations = self.session.query(Participation)\
         .filter(Participation.user_id == user_id)\
         .filter(Participation.contest_id == contest_id).all()
     self.assertEqual(len(db_participations), 1)
     p = db_participations[0]
     self.assertTrue(validate_password(p.password, password))
     self.assertEqual(p.hidden, hidden)
     self.assertEqual(p.unrestricted, unrestricted)
     if ip is None:
         self.assertIsNone(p.ip)
     else:
         self.assertCountEqual(p.ip, ip)
     if team_code is None:
         self.assertIsNone(p.team)
     else:
         self.assertEqual(p.team.code, team_code)
Ejemplo n.º 2
0
Archivo: main.py Proyecto: valiro21/cms
    def post(self):
        username = self.get_argument("username", "")
        password = self.get_argument("password", "")
        next_page = self.get_argument("next", "/")
        user = self.sql_session.query(User)\
            .filter(User.username == username)\
            .first()
        participation = self.sql_session.query(Participation)\
            .filter(Participation.contest == self.contest)\
            .filter(Participation.user == user)\
            .first()

        if user is None:
            # TODO: notify the user that they don't exist
            self.redirect("/?login_error=true")
            return

        if participation is None:
            # TODO: notify the user that they're uninvited
            self.redirect("/?login_error=true")
            return

        # If a contest-specific password is defined, use that. If it's
        # not, use the user's main password.
        if participation.password is None:
            correct_password = user.password
        else:
            correct_password = participation.password

        filtered_user = filter_ascii(username)
        filtered_pass = filter_ascii(password)

        if not validate_password(correct_password, password):
            logger.info("Login error: user=%s pass=%s remote_ip=%s." %
                        (filtered_user, filtered_pass, self.request.remote_ip))
            self.redirect("/?login_error=true")
            return

        if self.contest.ip_restriction and participation.ip is not None \
                and not check_ip(self.request.remote_ip, participation.ip):
            logger.info("Unexpected IP: user=%s pass=%s remote_ip=%s.",
                        filtered_user, filtered_pass, self.request.remote_ip)
            self.redirect("/?login_error=true")
            return

        if participation.hidden and self.contest.block_hidden_participations:
            logger.info("Hidden user login attempt: "
                        "user=%s pass=%s remote_ip=%s.",
                        filtered_user, filtered_pass, self.request.remote_ip)
            self.redirect("/?login_error=true")
            return

        logger.info("User logged in: user=%s remote_ip=%s.",
                    filtered_user, self.request.remote_ip)
        self.set_secure_cookie("login",
                               pickle.dumps((user.username,
                                             correct_password,
                                             make_timestamp())),
                               expires_days=None)
        self.redirect(next_page)
Ejemplo n.º 3
0
def safe_validate_password(participation, password):
    """Check that the password is correct for the authentication.

    Validate the given password against the participation (using either
    the global or the contest-specific password that is stored in the
    database), and guard against a misconfiguration.

    participation (Participation): a participation.
    password (str): a password provided by someone trying to log in
        claiming to be the given participation.

    return (bool): whether the password matches the expected one.

    """
    if participation.password is None:
        correct_password = participation.user.password
    else:
        correct_password = participation.password

    try:
        password_valid = validate_password(correct_password, password)
    except ValueError as e:
        # This is either a programming or a configuration error.
        logger.warning(
            "Invalid password stored in database for user %s in contest %s: "
            "%s", participation.user.username, participation.contest.name, e)
        return False

    return password_valid
Ejemplo n.º 4
0
def safe_validate_password(participation, password):
    """Check that the password is correct for the authentication.

    Validate the given password against the participation (using either
    the global or the contest-specific password that is stored in the
    database), and guard against a misconfiguration.

    participation (Participation): a participation.
    password (str): a password provided by someone trying to log in
        claiming to be the given participation.

    return (bool): whether the password matches the expected one.

    """
    if participation.password is None:
        correct_password = participation.user.password
    else:
        correct_password = participation.password

    try:
        password_valid = validate_password(correct_password, password)
    except ValueError as e:
        # This is either a programming or a configuration error.
        logger.warning(
            "Invalid password stored in database for user %s in contest %s: "
            "%s", participation.user.username, participation.contest.name, e)
        return False

    return password_valid
Ejemplo n.º 5
0
    def post(self):
        username = self.get_argument("username", "")
        password = self.get_argument("password", "")
        next_page = self.get_argument("next", "/")
        admin = self.sql_session.query(Admin)\
            .filter(Admin.username == username)\
            .first()

        if admin is None:
            logger.warning("Nonexistent admin account: %s", username)
            self.redirect("/login?login_error=true")
            return

        try:
            allowed = validate_password(admin.authentication, password)
        except ValueError:
            logger.warning("Unable to validate password for admin %s",
                           filter_ascii(username), exc_info=True)
            allowed = False

        if not allowed or not admin.enabled:
            if not allowed:
                logger.info("Login error for admin %s from IP %s.",
                            filter_ascii(username), self.request.remote_ip)
            elif not admin.enabled:
                logger.info("Login successful for admin %s from IP %s, "
                            "but account is disabled.",
                            filter_ascii(username), self.request.remote_ip)
            self.redirect("/login?login_error=true")
            return

        logger.info("Admin logged in: %s from IP %s.",
                    filter_ascii(username), self.request.remote_ip)
        self.service.auth_handler.set(admin.id)
        self.redirect(next_page)
Ejemplo n.º 6
0
 def assertAdminInDb(self, username, pwd, name, enabled, permission_all):
     """Assert that the admin with the given data is in the DB."""
     db_admins = self.session.query(Admin)\
         .filter(Admin.username == username).all()
     self.assertEqual(len(db_admins), 1)
     a = db_admins[0]
     self.assertTrue(validate_password(a.authentication, pwd))
     self.assertEqual(a.name, name)
     self.assertEqual(a.enabled, enabled)
     self.assertEqual(a.permission_all, permission_all)
Ejemplo n.º 7
0
 def assertAdminInDb(self, username, pwd, name, enabled, permission_all):
     """Assert that the admin with the given data is in the DB."""
     db_admins = self.session.query(Admin)\
         .filter(Admin.username == username).all()
     self.assertEqual(len(db_admins), 1)
     a = db_admins[0]
     self.assertTrue(validate_password(a.authentication, pwd))
     self.assertEqual(a.name, name)
     self.assertEqual(a.enabled, enabled)
     self.assertEqual(a.permission_all, permission_all)
Ejemplo n.º 8
0
def perform_login(username, password):
    with SessionGen() as session:
        # attempt to get admin for username & check if enabled
        admin = session.query(Admin).filter_by(username=username).one_or_none()
        if admin is None or not admin.enabled: abort(401)

        # attempt to validate password
        try:
            allowed = validate_password(admin.authentication, password)
        except ValueError:
            allowed = False
        if not allowed: abort(401)

        return admin.id
Ejemplo n.º 9
0
    def _get_user(self):
        username = self.get_argument("username")
        password = self.get_argument("password")

        # Find user if it exists
        user = self.sql_session.query(User)\
                        .filter(User.username == username)\
                        .first()
        if user is None:
            raise tornado_web.HTTPError(404)

        # Check if password is correct
        if not validate_password(user.password, password):
            raise tornado_web.HTTPError(403)

        return user
Ejemplo n.º 10
0
    def post(self):
        error_args = {"login_error": "true"}
        next_page = self.get_argument("next", None)
        if next_page is not None:
            error_args["next"] = next_page
            if next_page != "/":
                next_page = self.url(*next_page.strip("/").split("/"))
            else:
                next_page = self.url()
        else:
            next_page = self.url()
        error_page = self.url("login", **error_args)

        username = self.get_argument("username", "")
        password = self.get_argument("password", "")
        admin = self.sql_session.query(Admin)\
            .filter(Admin.username == username)\
            .first()

        if admin is None:
            logger.warning("Nonexistent admin account: %s", username)
            self.redirect(error_page)
            return

        try:
            allowed = validate_password(admin.authentication, password)
        except ValueError:
            logger.warning("Unable to validate password for admin %r",
                           username,
                           exc_info=True)
            allowed = False

        if not allowed or not admin.enabled:
            if not allowed:
                logger.info("Login error for admin %r from IP %s.", username,
                            self.request.remote_ip)
            elif not admin.enabled:
                logger.info(
                    "Login successful for admin %r from IP %s, but "
                    "account is disabled.", username, self.request.remote_ip)
            self.redirect(error_page)
            return

        logger.info("Admin logged in: %r from IP %s.", username,
                    self.request.remote_ip)
        self.service.auth_handler.set(admin.id)
        self.redirect(next_page)
Ejemplo n.º 11
0
Archivo: main.py Proyecto: cms-dev/cms
    def post(self):
        error_args = {"login_error": "true"}
        next_page = self.get_argument("next", None)
        if next_page is not None:
            error_args["next"] = next_page
            if next_page != "/":
                next_page = self.url(*next_page.strip("/").split("/"))
            else:
                next_page = self.url()
        else:
            next_page = self.url()
        error_page = self.url("login", **error_args)

        username = self.get_argument("username", "")
        password = self.get_argument("password", "")
        admin = self.sql_session.query(Admin)\
            .filter(Admin.username == username)\
            .first()

        if admin is None:
            logger.warning("Nonexistent admin account: %s", username)
            self.redirect(error_page)
            return

        try:
            allowed = validate_password(admin.authentication, password)
        except ValueError:
            logger.warning("Unable to validate password for admin %r", username,
                           exc_info=True)
            allowed = False

        if not allowed or not admin.enabled:
            if not allowed:
                logger.info("Login error for admin %r from IP %s.", username,
                            self.request.remote_ip)
            elif not admin.enabled:
                logger.info("Login successful for admin %r from IP %s, but "
                            "account is disabled.", username,
                            self.request.remote_ip)
            self.redirect(error_page)
            return

        logger.info("Admin logged in: %r from IP %s.", username,
                    self.request.remote_ip)
        self.service.auth_handler.set(admin.id)
        self.redirect(next_page)
Ejemplo n.º 12
0
 def assertParticipationInDb(self, user_id, contest_id, password,
                             delay_time=0, extra_time=0,
                             hidden=False, unrestricted=False,
                             ip=None, team_code=None):
     """Assert that the participation with the given data is in the DB."""
     db_participations = self.session.query(Participation)\
         .filter(Participation.user_id == user_id)\
         .filter(Participation.contest_id == contest_id).all()
     self.assertEqual(len(db_participations), 1)
     p = db_participations[0]
     self.assertTrue(validate_password(p.password, password))
     self.assertEqual(p.hidden, hidden)
     self.assertEqual(p.unrestricted, unrestricted)
     if ip is None:
         self.assertIsNone(p.ip)
     else:
         assertCountEqual(self, p.ip, ip)
     if team_code is None:
         self.assertIsNone(p.team)
     else:
         self.assertEqual(p.team.code, team_code)
Ejemplo n.º 13
0
 def test_plaintext(self):
     self.assertTrue(validate_password(
         hash_password("p你好", method="plaintext"), "p你好"))
     self.assertFalse(validate_password(
         hash_password("p你好", method="plaintext"), "你好"))
Ejemplo n.º 14
0
def validate_login(
        sql_session, contest, timestamp, username, password, ip_address):
    """Authenticate a user logging in, with username and password.

    Given the information the user provided (the username and the
    password) and some context information (contest, to determine which
    users are allowed to log in, how and with which restrictions;
    timestamp for cookie creation; IP address to check against) try to
    authenticate the user and return its participation and the cookie
    to set to help authenticate future visits.

    After finding the participation, IP login and hidden users
    restrictions are checked.

    sql_session (Session): the SQLAlchemy database session used to
        execute queries.
    contest (Contest): the contest the user is trying to access.
    timestamp (datetime): the date and the time of the request.
    username (str): the username the user provided.
    password (str): the password the user provided.
    ip_address (IPv4Address|IPv6Address): the IP address the request
        came from.

    return ((Participation, bytes)|(None, None)): if the user couldn't
        be authenticated then return None, otherwise return the
        participation that they wanted to authenticate as; if a cookie
        has to be set return it as well, otherwise return None.

    """
    def log_failed_attempt(msg, *args):
        logger.info("Unsuccessful login attempt from IP address %s, as user "
                    "%r, on contest %s, at %s: " + msg, ip_address,
                    username, contest.name, timestamp, *args)

    if not contest.allow_password_authentication:
        log_failed_attempt("password authentication not allowed")
        return None, None

    participation = sql_session.query(Participation) \
        .join(Participation.user) \
        .options(contains_eager(Participation.user)) \
        .filter(Participation.contest == contest)\
        .filter(User.username == username)\
        .first()

    if participation is None:
        log_failed_attempt("user not registered to contest")
        return None, None

    correct_password = get_password(participation)

    try:
        password_valid = validate_password(correct_password, password)
    except ValueError as e:
        # This is either a programming or a configuration error.
        logger.warning(
            "Invalid password stored in database for user %s in contest %s: "
            "%s", participation.user.username, participation.contest.name, e)
        return None, None

    if not password_valid:
        log_failed_attempt("wrong password")
        return None, None

    if contest.ip_restriction and participation.ip is not None \
            and not any(ip_address in network for network in participation.ip):
        log_failed_attempt("unauthorized IP address")
        return None, None

    if contest.block_hidden_participations and participation.hidden:
        log_failed_attempt("participation is hidden and unauthorized")
        return None, None

    logger.info("Successful login attempt from IP address %s, as user %r, on "
                "contest %s, at %s", ip_address, username, contest.name,
                timestamp)

    # If hashing is used, the cookie stores the hashed password so that
    # the expensive bcrypt call doesn't need to be done at every request.
    return (participation,
            json.dumps([username, correct_password, make_timestamp(timestamp)])
                .encode("utf-8"))
Ejemplo n.º 15
0
    def post(self):
        error_args = {"login_error": "true"}
        next_page = self.get_argument("next", None)
        if next_page is not None:
            error_args["next"] = next_page
            if next_page != "/":
                next_page = self.url(*next_page.strip("/").split("/"))
            else:
                next_page = self.url()
        else:
            next_page = self.contest_url()
        error_page = self.contest_url(**error_args)

        username = self.get_argument("username", "")
        password = self.get_argument("password", "")
        user = self.sql_session.query(User)\
            .filter(User.username == username)\
            .first()
        participation = self.sql_session.query(Participation)\
            .filter(Participation.contest == self.contest)\
            .filter(Participation.user == user)\
            .first()

        if user is None:
            # TODO: notify the user that they don't exist
            self.redirect(error_page)
            return

        if participation is None:
            # TODO: notify the user that they're uninvited
            self.redirect(error_page)
            return

        # If a contest-specific password is defined, use that. If it's
        # not, use the user's main password.
        if participation.password is None:
            correct_password = user.password
        else:
            correct_password = participation.password

        filtered_user = filter_ascii(username)
        filtered_pass = filter_ascii(password)

        if not validate_password(correct_password, password):
            logger.info("Login error: user=%s pass=%s remote_ip=%s." %
                        (filtered_user, filtered_pass, self.request.remote_ip))
            self.redirect(error_page)
            return

        if self.contest.ip_restriction and participation.ip is not None \
                and not check_ip(self.request.remote_ip, participation.ip):
            logger.info("Unexpected IP: user=%s pass=%s remote_ip=%s.",
                        filtered_user, filtered_pass, self.request.remote_ip)
            self.redirect(error_page)
            return

        if participation.hidden and self.contest.block_hidden_participations:
            logger.info(
                "Hidden user login attempt: "
                "user=%s pass=%s remote_ip=%s.", filtered_user, filtered_pass,
                self.request.remote_ip)
            self.redirect(error_page)
            return

        logger.info("User logged in: user=%s remote_ip=%s.", filtered_user,
                    self.request.remote_ip)
        self.set_secure_cookie(self.contest.name + "_login",
                               pickle.dumps((user.username, correct_password,
                                             make_timestamp())),
                               expires_days=None)
        self.redirect(next_page)
Ejemplo n.º 16
0
 def test_invalid_method(self):
     with self.assertRaises(ValueError):
         hash_password("test", "pwd")
     with self.assertRaises(ValueError):
         validate_password("test:pwd", "pwd")
Ejemplo n.º 17
0
 def test_bcrypt(self):
     self.assertTrue(
         validate_password(hash_password("p你好", method="bcrypt"), "p你好"))
     self.assertFalse(
         validate_password(hash_password("p你好", method="bcrypt"), "你好"))
Ejemplo n.º 18
0
 def test_invalid_method(self):
     with self.assertRaises(ValueError):
         hash_password("test", "pwd")
     with self.assertRaises(ValueError):
         validate_password("test:pwd", "pwd")
Ejemplo n.º 19
0
def validate_login(sql_session, contest, timestamp, username, password,
                   ip_address):
    """Authenticate a user logging in, with username and password.

    Given the information the user provided (the username and the
    password) and some context information (contest, to determine which
    users are allowed to log in, how and with which restrictions;
    timestamp for cookie creation; IP address to check against) try to
    authenticate the user and return its participation and the cookie
    to set to help authenticate future visits.

    After finding the participation, IP login and hidden users
    restrictions are checked.

    sql_session (Session): the SQLAlchemy database session used to
        execute queries.
    contest (Contest): the contest the user is trying to access.
    timestamp (datetime): the date and the time of the request.
    username (str): the username the user provided.
    password (str): the password the user provided.
    ip_address (IPv4Address|IPv6Address): the IP address the request
        came from.

    return ((Participation, bytes)|(None, None)): if the user couldn't
        be authenticated then return None, otherwise return the
        participation that they wanted to authenticate as; if a cookie
        has to be set return it as well, otherwise return None.

    """
    def log_failed_attempt(msg, *args):
        logger.info(
            "Unsuccessful login attempt from IP address %s, as user "
            "%r, on contest %s, at %s: " + msg, ip_address, username,
            contest.name, timestamp, *args)

    if not contest.allow_password_authentication:
        log_failed_attempt("password authentication not allowed")
        return None, None

    participation = sql_session.query(Participation) \
        .join(Participation.user) \
        .options(contains_eager(Participation.user)) \
        .filter(Participation.contest == contest)\
        .filter(User.username == username)\
        .first()

    if participation is None:
        log_failed_attempt("user not registered to contest")
        return None, None

    correct_password = get_password(participation)

    try:
        password_valid = validate_password(correct_password, password)
    except ValueError as e:
        # This is either a programming or a configuration error.
        logger.warning(
            "Invalid password stored in database for user %s in contest %s: "
            "%s", participation.user.username, participation.contest.name, e)
        return None, None

    if not password_valid:
        log_failed_attempt("wrong password")
        return None, None

    if contest.ip_restriction and participation.ip is not None \
            and not any(ip_address in network for network in participation.ip):
        log_failed_attempt("unauthorized IP address")
        return None, None

    if contest.block_hidden_participations and participation.hidden:
        log_failed_attempt("participation is hidden and unauthorized")
        return None, None

    logger.info(
        "Successful login attempt from IP address %s, as user %r, on "
        "contest %s, at %s", ip_address, username, contest.name, timestamp)

    # If hashing is used, the cookie stores the hashed password so that
    # the expensive bcrypt call doesn't need to be done at every request.
    return (participation,
            json.dumps([username, correct_password,
                        make_timestamp(timestamp)]).encode("utf-8"))
Ejemplo n.º 20
0
 def test_plaintext(self):
     self.assertTrue(
         validate_password(hash_password("p你好", method="plaintext"), "p你好"))
     self.assertFalse(
         validate_password(hash_password("p你好", method="plaintext"), "你好"))
Ejemplo n.º 21
0
 def test_bcrypt(self):
     self.assertTrue(validate_password(
         hash_password("p你好", method="bcrypt"), "p你好"))
     self.assertFalse(validate_password(
         hash_password("p你好", method="bcrypt"), "你好"))