diff --git a/cms/db/__init__.py b/cms/db/__init__.py index 85a734da77..31be9a62c5 100644 --- a/cms/db/__init__.py +++ b/cms/db/__init__.py @@ -46,7 +46,7 @@ # session "Session", "ScopedSession", "SessionGen", "custom_psycopg2_connection", # types - "CastingArray", "Codename", "Filename", "FilenameSchema", + "CastingArray", "Codename", "Username", "Filename", "FilenameSchema", "FilenameSchemaArray", "Digest", # base "metadata", "Base", @@ -91,7 +91,7 @@ from .session import Session, ScopedSession, SessionGen, \ custom_psycopg2_connection -from .types import CastingArray, Codename, Filename, FilenameSchema, \ +from .types import CastingArray, Codename, Username, Filename, FilenameSchema, \ FilenameSchemaArray, Digest from .base import Base from .fsobject import FSObject, LargeObject diff --git a/cms/db/base.py b/cms/db/base.py index 41c64ad143..836c807340 100644 --- a/cms/db/base.py +++ b/cms/db/base.py @@ -32,7 +32,7 @@ Boolean, Integer, Float, String, Unicode, Enum, DateTime, Interval, \ BigInteger -from . import engine, metadata, CastingArray, Codename, Filename, \ +from . import engine, metadata, CastingArray, Codename, Username, Filename, \ FilenameSchema, FilenameSchemaArray, Digest @@ -46,6 +46,7 @@ Unicode: str, String: str, # TODO Use bytes. Codename: str, + Username: str, Filename: str, FilenameSchema: str, Digest: str, diff --git a/cms/db/types.py b/cms/db/types.py index 7c76a1a7eb..e863297715 100644 --- a/cms/db/types.py +++ b/cms/db/types.py @@ -48,6 +48,37 @@ def bind_expression(self, bindvalue): return sqlalchemy.cast(bindvalue, self) +class Username(TypeDecorator): + """Check that the column uses a limited alphabet. + + Namely: latin letters (upper and lowercase), arabic digits, the + underscore, the period, and the dash. It must also be non-empty. + + """ + + domain_name = "USERNAME" + impl = Unicode + + @classmethod + def get_create_command(cls): + return DDL("CREATE DOMAIN %(domain)s VARCHAR " + "CHECK (VALUE ~ '^[A-Za-z0-9_.-]+$')", + context={"domain": cls.domain_name}) + + @classmethod + def get_drop_command(cls): + return DDL("DROP DOMAIN %(domain)s", + context={"domain": cls.domain_name}) + +event.listen(metadata, "before_create", Username.get_create_command()) +event.listen(metadata, "after_drop", Username.get_drop_command()) + + +@compiles(Username) +def compile_codename(element, compiler, **kw): + return Username.domain_name + + class Codename(TypeDecorator): """Check that the column uses a limited alphabet. diff --git a/cms/db/user.py b/cms/db/user.py index bc1dcc5d8c..33f7365f71 100644 --- a/cms/db/user.py +++ b/cms/db/user.py @@ -35,7 +35,7 @@ Interval from cmscommon.crypto import generate_random_password, build_password -from . import CastingArray, Codename, Base, Admin, Contest +from . import CastingArray, Codename, Username, Base, Admin, Contest class User(Base): @@ -60,7 +60,7 @@ class User(Base): # Username and password to log in the CWS. username = Column( - Codename, + Username, nullable=False, unique=True) password = Column( diff --git a/cms/grading/steps/compilation.py b/cms/grading/steps/compilation.py index e79e97d3b0..62cf2b4780 100644 --- a/cms/grading/steps/compilation.py +++ b/cms/grading/steps/compilation.py @@ -99,7 +99,7 @@ def compilation_step(sandbox, commands): sandbox.preserve_env = True sandbox.max_processes = config.compilation_sandbox_max_processes sandbox.timeout = config.compilation_sandbox_max_time_s - sandbox.wallclock_timeout = 2 * sandbox.timeout + 1 + sandbox.wallclock_timeout = 10 * sandbox.timeout + 1 sandbox.address_space = config.compilation_sandbox_max_memory_kib * 1024 # Run the compilation commands, copying stdout and stderr to stats. diff --git a/cms/grading/steps/evaluation.py b/cms/grading/steps/evaluation.py index 997d5f1ca9..4760538796 100644 --- a/cms/grading/steps/evaluation.py +++ b/cms/grading/steps/evaluation.py @@ -179,7 +179,7 @@ def evaluation_step_before_run(sandbox, command, # Set sandbox parameters suitable for evaluation. if time_limit is not None: sandbox.timeout = time_limit - sandbox.wallclock_timeout = 2 * time_limit + 1 + sandbox.wallclock_timeout = 10 * time_limit + 1 else: sandbox.timeout = None sandbox.wallclock_timeout = None diff --git a/cms/grading/steps/trusted.py b/cms/grading/steps/trusted.py index 41abc25044..350919c64d 100644 --- a/cms/grading/steps/trusted.py +++ b/cms/grading/steps/trusted.py @@ -144,7 +144,7 @@ def trusted_step(sandbox, commands): sandbox.preserve_env = True sandbox.max_processes = config.trusted_sandbox_max_processes sandbox.timeout = config.trusted_sandbox_max_time_s - sandbox.wallclock_timeout = 2 * sandbox.timeout + 1 + sandbox.wallclock_timeout = 10 * sandbox.timeout + 1 sandbox.address_space = config.trusted_sandbox_max_memory_kib * 1024 # Run the trusted commands. diff --git a/cmscontrib/DumpExporter.py b/cmscontrib/DumpExporter.py index 926edc4abc..e3ca36fb2d 100755 --- a/cmscontrib/DumpExporter.py +++ b/cmscontrib/DumpExporter.py @@ -44,7 +44,7 @@ from sqlalchemy.dialects.postgresql import ARRAY, CIDR, JSONB from cms import rmtree, utf8_decoder -from cms.db import version as model_version, Codename, Filename, \ +from cms.db import version as model_version, Codename, Username, Filename, \ FilenameSchema, FilenameSchemaArray, Digest, SessionGen, Contest, User, \ Task, Submission, UserTest, SubmissionResult, UserTestResult, PrintJob, \ Announcement, Participation, enumerate_files @@ -113,7 +113,7 @@ def encode_value(type_, value): return None elif isinstance(type_, ( Boolean, Integer, Float, String, Unicode, Enum, JSONB, Codename, - Filename, FilenameSchema, Digest)): + Username, Filename, FilenameSchema, Digest)): return value elif isinstance(type_, DateTime): return make_timestamp(value) diff --git a/cmscontrib/DumpImporter.py b/cmscontrib/DumpImporter.py index 659bfa2edc..2c43d3abc5 100755 --- a/cmscontrib/DumpImporter.py +++ b/cmscontrib/DumpImporter.py @@ -47,7 +47,7 @@ import cms.db as class_hook from cms import utf8_decoder -from cms.db import version as model_version, Codename, Filename, \ +from cms.db import version as model_version, Codename, Username, Filename, \ FilenameSchema, FilenameSchemaArray, Digest, SessionGen, Contest, \ Submission, SubmissionResult, User, Participation, UserTest, \ UserTestResult, PrintJob, Announcement, init_db, drop_db, enumerate_files @@ -98,7 +98,7 @@ def decode_value(type_, value): return None elif isinstance(type_, ( Boolean, Integer, Float, String, Unicode, Enum, JSONB, Codename, - Filename, FilenameSchema, Digest)): + Username, Filename, FilenameSchema, Digest)): return value elif isinstance(type_, DateTime): try: diff --git a/cmscontrib/loaders/kompgen.py b/cmscontrib/loaders/kompgen.py index d37b80e396..6e7b10e3c1 100644 --- a/cmscontrib/loaders/kompgen.py +++ b/cmscontrib/loaders/kompgen.py @@ -77,6 +77,7 @@ def __init__(self, input=None, output=None): 'c++': 'C++17 / g++', 'cpp': 'C++17 / g++', 'java': 'Java / JDK', + 'python3.11': 'Python 3.11 / CPython', 'python3': 'Python 3 / CPython', 'pypy3': 'Python 3 / PyPy', } diff --git a/cmsranking/static/img/logo.png b/cmsranking/static/img/logo.png index 5927722204..a4faf68aef 100644 Binary files a/cmsranking/static/img/logo.png and b/cmsranking/static/img/logo.png differ diff --git a/requirements.txt b/requirements.txt index 1c54fa38ee..ce452c1c0c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ psycopg2>=2.8,<2.9 # http://initd.org/psycopg/articles/tag/release/ sqlalchemy>=1.3,<1.4 # http://docs.sqlalchemy.org/en/latest/changelog/index.html netifaces>=0.10,<0.11 # https://bitbucket.org/al45tair/netifaces/src/ pycryptodomex>=3.6,<3.7 # https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst -psutil>=5.5,<5.6 # https://github.com/giampaolo/psutil/blob/master/HISTORY.rst +psutil>=5.6.1,<5.7 # https://github.com/giampaolo/psutil/blob/master/HISTORY.rst requests>=2.22,<2.23 # https://pypi.python.org/pypi/requests # Slightly higher version for Python 3.8 support gevent>=1.5,<1.6 # http://www.gevent.org/changelog.html