AddParticipation.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. #!/usr/bin/env python3
  2. # Contest Management System - http://cms-dev.github.io/
  3. # Copyright © 2017 Stefano Maggiolo <s.maggiolo@gmail.com>
  4. # Copyright © 2016 Myungwoo Chun <mc.tamaki@gmail.com>
  5. # Copyright © 2017 Luca Wehrstedt <luca.wehrstedt@gmail.com>
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU Affero General Public License as
  9. # published by the Free Software Foundation, either version 3 of the
  10. # License, or (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU Affero General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU Affero General Public License
  18. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. """This script creates a new participation in the database.
  20. """
  21. # We enable monkey patching to make many libraries gevent-friendly
  22. # (for instance, urllib3, used by requests)
  23. import gevent.monkey
  24. gevent.monkey.patch_all() # noqa
  25. import argparse
  26. import datetime
  27. import ipaddress
  28. import logging
  29. import sys
  30. from sqlalchemy.exc import IntegrityError
  31. from cms import utf8_decoder
  32. from cms.db import Contest, Participation, SessionGen, Team, User, \
  33. ask_for_contest
  34. from cmscommon.crypto import build_password, hash_password
  35. logger = logging.getLogger(__name__)
  36. def add_participation(username, contest_id, ip, delay_time, extra_time,
  37. password, method, is_hashed, team_code, hidden,
  38. unrestricted):
  39. logger.info("Creating the user's participation in the database.")
  40. delay_time = delay_time if delay_time is not None else 0
  41. extra_time = extra_time if extra_time is not None else 0
  42. if hidden:
  43. logger.warning("The participation will be hidden")
  44. if unrestricted:
  45. logger.warning("The participation will be unrestricted")
  46. try:
  47. with SessionGen() as session:
  48. user = \
  49. session.query(User).filter(User.username == username).first()
  50. if user is None:
  51. logger.error("No user with username `%s' found.", username)
  52. return False
  53. contest = Contest.get_from_id(contest_id, session)
  54. if contest is None:
  55. logger.error("No contest with id `%s' found.", contest_id)
  56. return False
  57. team = None
  58. if team_code is not None:
  59. team = \
  60. session.query(Team).filter(Team.code == team_code).first()
  61. if team is None:
  62. logger.error("No team with code `%s' found.", team_code)
  63. return False
  64. if password is not None:
  65. if is_hashed:
  66. password = build_password(password, method)
  67. else:
  68. password = hash_password(password, method)
  69. participation = Participation(
  70. user=user,
  71. contest=contest,
  72. ip=[ipaddress.ip_network(ip)] if ip is not None else None,
  73. delay_time=datetime.timedelta(seconds=delay_time),
  74. extra_time=datetime.timedelta(seconds=extra_time),
  75. password=password,
  76. team=team,
  77. hidden=hidden,
  78. unrestricted=unrestricted)
  79. session.add(participation)
  80. session.commit()
  81. except IntegrityError:
  82. logger.error("A participation for this user in this contest "
  83. "already exists.")
  84. return False
  85. logger.info("Participation added.")
  86. return True
  87. def main():
  88. """Parse arguments and launch process.
  89. """
  90. parser = argparse.ArgumentParser(description="Add a participation to CMS.")
  91. parser.add_argument("username", action="store", type=utf8_decoder,
  92. help="username to add to the contest")
  93. parser.add_argument("-c", "--contest-id", action="store", type=int,
  94. help="id of the contest the users will be attached to")
  95. parser.add_argument("-i", "--ip", action="store", type=utf8_decoder,
  96. help="ip address of this user")
  97. parser.add_argument("-d", "--delay_time", action="store", type=int,
  98. help="how much the contest is shifted, in seconds")
  99. parser.add_argument("-e", "--extra_time", action="store", type=int,
  100. help="how much additional time, in seconds")
  101. parser.add_argument("-t", "--team", action="store", type=utf8_decoder,
  102. help="code of the team for this participation")
  103. parser.add_argument("--hidden", action="store_true",
  104. help="if the participation is hidden")
  105. parser.add_argument("--unrestricted", action="store_true",
  106. help="if the participation is unrestricted")
  107. password_group = parser.add_mutually_exclusive_group()
  108. password_group.add_argument(
  109. "-p", "--plaintext-password", action="store", type=utf8_decoder,
  110. help="password of the user in plain text")
  111. password_group.add_argument(
  112. "-H", "--hashed-password", action="store", type=utf8_decoder,
  113. help="password of the user, already hashed using the given algorithm "
  114. "(currently only --bcrypt)")
  115. method_group = parser.add_mutually_exclusive_group()
  116. method_group.add_argument(
  117. "--bcrypt", dest="method", action="store_const", const="bcrypt",
  118. help="whether the password will be stored in bcrypt-hashed format "
  119. "(if omitted it will be stored in plain text)")
  120. args = parser.parse_args()
  121. if args.hashed_password is not None and args.method is None:
  122. parser.error("hashed password given but no method specified")
  123. if args.contest_id is None:
  124. args.contest_id = ask_for_contest()
  125. success = add_participation(
  126. args.username, args.contest_id,
  127. args.ip, args.delay_time, args.extra_time,
  128. args.plaintext_password or args.hashed_password,
  129. args.method or "plaintext",
  130. args.hashed_password is not None, args.team,
  131. args.hidden, args.unrestricted)
  132. return 0 if success is True else 1
  133. if __name__ == "__main__":
  134. sys.exit(main())