Exemple #1
0
    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 password != correct_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)
Exemple #2
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)
Exemple #3
0
    def post(self):
        username = self.get_argument("username", "")
        password = self.get_argument("password", "")
        user = self.sql_session.query(User)\
            .filter(User.auth_type == "Password")\
            .filter(User.contest == self.contest)\
            .filter(User.username == username).first()

        filtered_user = filter_ascii(username)
        filtered_pass = filter_ascii(password)
        if user is None or user.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

        self.try_user_login(user)
Exemple #4
0
    def _on_auth(self, user_t):
        if not user_t:
            raise tornado.web.HTTPError(500, "Google auth failed")

        username = user_t["email"]
        user = self.sql_session.query(User)\
            .filter(User.auth_type == "Google")\
            .filter(User.contest == self.contest)\
            .filter(User.username == username).first()

        filtered_user = filter_ascii(username)
        if user is None:
            user = User(user_t["first_name"],
                        user_t["last_name"],
                        username,
                        email=user_t["email"],
                        auth_type="Google",
                        contest=self.contest)
            self.sql_session.add(user)
            self.sql_session.commit()

        self.try_user_login(user)
Exemple #5
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.headers.get("X-Real-IP")
                         or 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.headers.get("X-Real-IP")
                or 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.headers.get("X-Real-IP")
                or self.request.remote_ip)
            self.redirect(error_page)
            return

        logger.info(
            "User logged in: user=%s remote_ip=%s.", filtered_user,
            self.request.headers.get("X-Real-IP") or 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)
Exemple #6
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.

    """
    filtered_user = filter_ascii(username)

    def log_failed_attempt(msg, *args):
        logger.info(
            "Unsuccessful login attempt from IP address %s, as user "
            "%s, on contest %s, at %s: " + msg, ip_address, filtered_user,
            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

    if not safe_validate_password(participation, password):
        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 %s, on "
        "contest %s, at %s", ip_address, filtered_user, contest.name,
        timestamp)

    return (participation,
            pickle.dumps((username, password, make_timestamp(timestamp))))
Exemple #7
0
def _authenticate_request_from_cookie(sql_session, contest, timestamp, cookie):
    """Return the current participation based on the cookie.

    If a participation can be extracted, the cookie is refreshed.

    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.
    cookie (bytes|None): the cookie the user's browser provided in the
        request (if any).

    return ((Participation, bytes)|(None, None)): the participation
        extracted from the cookie and the cookie to set/refresh, or
        None in case of errors.

    """
    if cookie is None:
        logger.info("Unsuccessful cookie authentication: no cookie provided")
        return None, None

    # Parse cookie.
    try:
        cookie = pickle.loads(cookie)
        username = cookie[0]
        password = cookie[1]
        last_update = make_datetime(cookie[2])
    except Exception as e:
        # Cookies are stored securely and thus cannot be tampered with:
        # this is either a programming or a configuration error.
        logger.warning("Invalid cookie (%s): %s", e, cookie)
        return None, None

    filtered_user = filter_ascii(username)

    def log_failed_attempt(msg, *args):
        logger.info(
            "Unsuccessful cookie authentication as %s, returning from "
            "%s, at %s: " + msg, filtered_user, last_update, timestamp, *args)

    # Check if the cookie is expired.
    if timestamp - last_update > timedelta(seconds=config.cookie_duration):
        log_failed_attempt("cookie expired (lasts %d seconds)",
                           config.cookie_duration)
        return None, None

    # Load participation from DB and make sure it exists.
    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

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

    logger.info(
        "Successful cookie authentication as user %s, on contest %s, "
        "returning from %s, at %s", filtered_user, contest.name, last_update,
        timestamp)

    return (participation,
            pickle.dumps((username, password, make_timestamp(timestamp))))