def post(self, request): """ 产生用户 """ data = request.data # 设置数字的最大长度100-105 number_max_length = max(len(str(data["number_from"])), len(str(data["number_to"]))) # 用户名字长度不应该大于32个字符 if number_max_length + len(data["prefix"]) + len(data["suffix"]) > 32: return self.error("Username should not more than 32 characters") if data["number_from"] > data["number_to"]: return self.error("Start number must be lower than end number") # 文件名字ID使用随机生成的8个字符 file_id = rand_str(8) filename = f"/tmp/{file_id}.xlsx" # 创建工作簿 workbook = xlsxwriter.Workbook(filename) # 工作簿中添加页 worksheet = workbook.add_worksheet() # 每一页设置29列A:B,A1和B1分别保存用户名和密码 worksheet.set_column("A:B", 20) worksheet.write("A1", "Username") worksheet.write("B1", "Password") i = 1 # 先初始化用户列表 user_list = [] # 假设每次创建from100个和to105个,用户名为prenumbersuf,每创建一个用户user,都添加到user_list里面去, # 原值密码生成之后,使用内置方法make_password保存到生成的每一个用户里面去 for number in range(data["number_from"], data["number_to"] + 1): raw_password = rand_str(data["password_length"]) user = User(username=f"{data['prefix']}{number}{data['suffix']}", password=make_password(raw_password)) user.raw_password = raw_password user_list.append(user) try: with transaction.atomic(): # 使用原子操作,将数据同一插入到数据库里面去,同事同步User和UserProfile这两张表, # 同事将数据写入到工作簿的页里面去,最后返回该工作页文件的ID ret = User.objects.bulk_create(user_list) UserProfile.objects.bulk_create([UserProfile(user=user) for user in ret]) for item in user_list: worksheet.write_string(i, 0, item.username) worksheet.write_string(i, 1, item.raw_password) i += 1 workbook.close() return self.success({"file_id": file_id}) except IntegrityError as e: # Extract detail from exception message # duplicate key value violates unique constraint "user_username_key" # DETAIL: Key (username)=(root11) already exists. <- this line return self.error(str(e).split("\n")[1])
def _set_tfa(self): # 开启双因素验证,注意这里生成的双因素验证码直接就根据对应的用户保存到数据库里面了 self.user.two_factor_auth = True tfa_token = rand_str(32) self.user.tfa_token = tfa_token self.user.save() return tfa_token
def post(self, request): form = UploadProblemForm(request.POST, request.FILES) if form.is_valid(): file = form.cleaned_data["file"] with tempfile.NamedTemporaryFile("wb") as tf: for chunk in file.chunks(4096): tf.file.write(chunk) problems = FPSParser(tf.name).parse() else: return self.error("Parse upload file error") helper = FPSHelper() with transaction.atomic(): for _problem in problems: test_case_id = rand_str() test_case_dir = os.path.join(settings.TEST_CASE_DIR, test_case_id) os.mkdir(test_case_dir) helper.save_test_case(_problem, test_case_dir) problem_data = helper.save_image(_problem, settings.UPLOAD_DIR, settings.UPLOAD_PREFIX) s = FPSProblemSerializer(data=problem_data) if not s.is_valid(): return self.error(f"Parse FPS file error: {s.errors}") problem_data = s.data problem_data["test_case_id"] = test_case_id self._create_problem(problem_data, request.user) return self.success({"import_count": len(problems)})
def post(self, request): data = request.data spj_version = rand_str(8) error = SPJCompiler(data["spj_code"], spj_version, data["spj_language"]).compile_spj() if error: return self.error(error) else: return self.success()
def post(self, request): user = request.user if not user.open_api: return self.error("OpenAPI function is truned off for you") api_appkey = rand_str() user.open_api_appkey = api_appkey user.save() return self.success({"appkey": api_appkey})
def _set_captcha(self, session): captcha = rand_str(4) # session存放生成的验证码 session["_django_captcha_key"] = captcha # session存放生成的验证码时间 session["_django_captcha_expires_time"] = int(time.time()) + 30 session.save() return captcha
def setUp(self): print("Testing", self._testMethodName) self.client = APIClient() self.register_url = self.reverse("user_register_api") self.captcha = rand_str(4) self.data = {"username": "******", "password": "******", "real_name": "real_name", "email": "*****@*****.**", "captcha": self._set_captcha(self.client.session)}
def test_tfa_code_required(self): # 测试要求使用双因素来修改密码 self.user.two_factor_auth = True self.user.tfa_token = "tfa_token" self.user.save() self.assertTrue(self.client.login(username=self.username, password=self.old_password)) # 自己新产生一个自然不能通过 self.data["tfa_code"] = rand_str(6) resp = self.client.post(self.url, data=self.data) self.assertEqual(resp.data, {"error": "error", "data": "Invalid two factor verification code"}) # 必须使用数据库存放的双因素验证码验证才可以 self.data["tfa_code"] = self._get_tfa_code() resp = self.client.post(self.url, data=self.data) self.assertSuccess(resp)
def post(self, request): # 首先判断是否登录 if request.user.is_authenticated(): return self.error( "You have already logged in, are you kidding me? ") # 获得请求数据 data = request.data # 获得验证码 captcha = Captcha(request) # 验证码不成功返回无效的验证码信息 if not captcha.check(data["captcha"]): return self.error("Invalid captcha") try: # 查询数据库获得邮箱,判断是否存在 user = User.objects.get(email__iexact=data["email"]) except User.DoesNotExist: return self.error("User does not exist") # 如果重置密码令牌失效,或者超过20分钟,返回错误提示 if user.reset_password_token_expire_time and 0 < int( (user.reset_password_token_expire_time - now()).total_seconds()) < 20 * 60: return self.error( "You can only reset password once per 20 minutes") # 更新重置密码令牌和失效时间 user.reset_password_token = rand_str() user.reset_password_token_expire_time = now() + timedelta(minutes=20) user.save() # render_data = { "username": user.username, "website_name": SysOptions.website_name, "link": f"{SysOptions.website_base_url}/reset-password/{user.reset_password_token}" } # render_to_string: Loads a template and renders it with a context. Returns a string. # 加载一个模板,转换成string,发送到对应邮箱 email_html = render_to_string("reset_password_email.html", render_data) send_email_async.delay(from_name=SysOptions.website_name_shortcut, to_email=user.email, to_name=user.username, subject=f"Reset your password", content=email_html) return self.success("Succeeded")
def get(self, request): """ 获取二维码 """ user = request.user if user.two_factor_auth: return self.error("2FA is already turned on") token = rand_str() user.tfa_token = token user.save() # 设置二维码的标签:网站名:用户名 label = f"{SysOptions.website_name_shortcut}:{user.username}" # 生成一张img2base64二维码:qrcode和optauth结合,转换成uri的形式 image = qrcode.make( OtpAuth(token).to_uri("totp", label, SysOptions.website_name.replace(" ", ""))) return self.success(img2base64(image))
def post(self, request): """ post方法请求上传图片 :param request: :return: """ form = ImageUploadForm(request.POST, request.FILES) # form有效,就上传成功,否则上传失败 if form.is_valid(): img = form.cleaned_data["image"] else: return self.response({ "success": False, "msg": "Upload failed", "file_path": ""}) # 截取图片的后缀,等到文件的名称 suffix = os.path.splitext(img.name)[-1].lower() # 如果不是其中的类型,就返回失败信息 if suffix not in [".gif", ".jpg", ".jpeg", ".bmp", ".png"]: return self.response({ "success": False, "msg": "Unsupported file format", "file_path": ""}) # 重新构造文件的名称 img_name = rand_str(10) + suffix try: # 尝试往上传文件的目录里面写入该上传的图片,成功就返回成功信息,否则就返回失败信息。 with open(os.path.join(settings.UPLOAD_DIR, img_name), "wb") as imgFile: for chunk in img: imgFile.write(chunk) except IOError as e: logger.error(e) return self.response({ "success": True, "msg": "Upload Error", "file_path": f"{settings.UPLOAD_PREFIX}/{img_name}"}) return self.response({ "success": True, "msg": "Success", "file_path": f"{settings.UPLOAD_PREFIX}/{img_name}"})
def _create_problem(self, problem_data, creator): if problem_data["time_limit"]["unit"] == "ms": time_limit = problem_data["time_limit"]["value"] else: time_limit = problem_data["time_limit"]["value"] * 1000 template = {} prepend = {} append = {} for t in problem_data["prepend"]: prepend[t["language"]] = t["code"] for t in problem_data["append"]: append[t["language"]] = t["code"] for t in problem_data["template"]: our_lang = lang = t["language"] if lang == "Python": our_lang = "Python3" template[our_lang] = TEMPLATE_BASE.format(prepend.get(lang, ""), t["code"], append.get(lang, "")) spj = problem_data["spj"] is not None Problem.objects.create(_id=f"fps-{rand_str(4)}", title=problem_data["title"], description=problem_data["description"], input_description=problem_data["input"], output_description=problem_data["output"], hint=problem_data["hint"], test_case_score=[], time_limit=time_limit, memory_limit=problem_data["memory_limit"]["value"], samples=problem_data["samples"], template=template, rule_type=ProblemRuleType.ACM, source=problem_data.get("source", ""), spj=spj, spj_code=problem_data["spj"]["code"] if spj else None, spj_language=problem_data["spj"]["language"] if spj else None, spj_version=rand_str(8) if spj else "", visible=False, languages=SysOptions.language_names, created_by=creator, difficulty=Difficulty.MID, test_case_id=problem_data["test_case_id"])
def post(self, request): form = ImageUploadForm(request.POST, request.FILES) if form.is_valid(): avatar = form.cleaned_data["image"] else: return self.error("Invalid file content") if avatar.size > 2 * 1024 * 1024: return self.error("Picture is too large") suffix = os.path.splitext(avatar.name)[-1].lower() if suffix not in [".gif", ".jpg", ".jpeg", ".bmp", ".png"]: return self.error("Unsupported file format") name = rand_str(10) + suffix with open(os.path.join(settings.AVATAR_UPLOAD_DIR, name), "wb") as img: for chunk in avatar: img.write(chunk) user_profile = request.user.userprofile user_profile.avatar = f"{settings.AVATAR_URI_PREFIX}/{name}" user_profile.save() return self.success("Succeeded")
def put(self, request): """ 编辑用户的接口api: 这里根据用户提交的数据,将数据设置保存到数据库 """ # 根据用户请求数据中的ID,查找数据库,看是否存在这样的用户(这里必须传入用户的ID作为唯一的标识) data = request.data try: user = User.objects.get(id=data["id"]) except User.DoesNotExist: return self.error("User does not exist") # 将用户名和邮箱的字母转化成小写然后再判断,大小写不区分 if User.objects.filter(username=data["username"].lower()).exclude(id=user.id).exists(): return self.error("Username already exists") if User.objects.filter(email=data["email"].lower()).exclude(id=user.id).exists(): return self.error("Email already exists") # 根据传入的数据进行赋值,注意这里的username要先保存起来备用 pre_username = user.username user.username = data["username"].lower() user.email = data["email"].lower() user.admin_type = data["admin_type"] user.is_disabled = data["is_disabled"] # 普通管理员和超级管理员判断 if data["admin_type"] == AdminType.ADMIN: user.problem_permission = data["problem_permission"] elif data["admin_type"] == AdminType.SUPER_ADMIN: user.problem_permission = ProblemPermission.ALL else: user.problem_permission = ProblemPermission.NONE if data["password"]: user.set_password(data["password"]) # 如果开放api,就是data["open_api"]为True if data["open_api"]: # 保存更改之后避免重置用户的appkey,open_api是boolean if not user.open_api: user.open_api_appkey = rand_str() else: user.open_api_appkey = None user.open_api = data["open_api"] # 如果需要双因素验证 # data["two_factor_auth"]为True时候就设置tfa_token if data["two_factor_auth"]: # 保存更改之后避免重置用户的tfa_token # 这里注意:是数据库中的用户two_factor_auth,而不是data的two_factor_auth,数据库中的默认是false if not user.two_factor_auth: user.tfa_token = rand_str() else: user.tfa_token = None user.two_factor_auth = data["two_factor_auth"] user.save() # 如果之前的用户名字和新的用户名字不同,更新提交信息里面的用户名 if pre_username != user.username: Submission.objects.filter(username=pre_username).update(username=user.username) # 用户信息表根据对应的用户,将和User表中相同的字段:real_name也更新 UserProfile.objects.filter(user=user).update(real_name=data["real_name"]) return self.success(UserAdminSerializer(user).data)
def get(self, request): token = rand_str() request.user.auth_token = token request.user.save() return self.success({"token": token})
def default_token(): token = os.environ.get("JUDGE_SERVER_TOKEN") return token if token else rand_str()
def process_zip(self, uploaded_zip_file, spj, dir=""): """ 这个方法被TestCaseAPI的post调用,用户解析用户上传传过来的zip文件 :param uploaded_zip_file: 用户上传的zip文件 :param spj: 特殊评判 :param dir: 默认为空 :return: """ try: # 以读的方式打开压缩文件,不能打开就报错 zip_file = zipfile.ZipFile(uploaded_zip_file, "r") except zipfile.BadZipFile: raise APIError("Bad zip file") # 获得压缩文件的名字列表,["1.in", "1.out", "2.in", ".DS_Store"] name_list = zip_file.namelist() # 调用filter_name_list函数过滤测试用例,过滤之后为的test_case_list为["1.in", "2.in"]->这里以特殊评判为例,如果为空就报错 # 如果是非特殊评判的就是:过滤之后为的test_case_list为["1.in", "2.out"] test_case_list = self.filter_name_list(name_list, spj=spj, dir=dir) if not test_case_list: raise APIError("Empty file") # 创建测试用例文件夹同时更改文件夹权限 test_case_id = rand_str() test_case_dir = os.path.join(settings.TEST_CASE_DIR, test_case_id) # /data/test_case/test_case_id os.mkdir(test_case_dir) os.chmod(test_case_dir, 0o710) # 设置缓存 size_cache = {} # 如果是spj,作为input_size md5_cache = {} for item in test_case_list: with open(os.path.join(test_case_dir, item), "wb") as f: # 拼接路径,例如:/data/test_case/test_case_id/1.in content = zip_file.read(f"{dir}{item}").replace(b"\r\n", b"\n") # 设置内容的大小 size_cache[item] = len(content) # 最终如果是spj, 用于内容的输入大小说明 if item.endswith(".out"): # 这个是对于普通评判来说,因为特殊评判没有.out文件,同时要生成摘要保存在字典先 md5_cache[item] = hashlib.md5(content.rstrip()).hexdigest() f.write(content) # 1.in \n 1.in\n # 设置测试用例备用 test_case_info = {"spj": spj, "test_cases": {}} # info列表备用 info = [] if spj: for index, item in enumerate(test_case_list): data = {"input_name": item, "input_size": size_cache[item]} # info文件拼接数据 info.append(data) # index是从0开始的,所以需要+1 test_case_info["test_cases"][str(index + 1)] = data else: # 输入输出情况 # ["1.in", "1.out", "2.in", "2.out"] => [("1.in", "1.out"), ("2.in", "2.out")] test_case_list = zip(*[test_case_list[i::2] for i in range(2)]) for index, item in enumerate(test_case_list): data = {"stripped_output_md5": md5_cache[item[1]], "input_size": size_cache[item[0]], "output_size": size_cache[item[1]], "input_name": item[0], "output_name": item[1]} info.append(data) test_case_info["test_cases"][str(index + 1)] = data # 真正打开Info文件的时候,将test_case_info写入到info文件里面去 with open(os.path.join(test_case_dir, "info"), "w", encoding="utf-8") as f: f.write(json.dumps(test_case_info, indent=4)) # 对于每个文件# /data/test_case/test_case_id都更改权限 # -1.in # -1.out # -info for item in os.listdir(test_case_dir): os.chmod(os.path.join(test_case_dir, item), 0o640) #返回信息文件和测试用例ID return info, test_case_id
def post(self, request): form = UploadProblemForm(request.POST, request.FILES) if form.is_valid(): file = form.cleaned_data["file"] tmp_file = f"/tmp/{rand_str()}.zip" with open(tmp_file, "wb") as f: for chunk in file: f.write(chunk) else: return self.error("Upload failed") count = 0 with zipfile.ZipFile(tmp_file, "r") as zip_file: name_list = zip_file.namelist() for item in name_list: if "/problem.json" in item: count += 1 with transaction.atomic(): for i in range(1, count + 1): with zip_file.open(f"{i}/problem.json") as f: problem_info = json.load(f) serializer = ImportProblemSerializer(data=problem_info) if not serializer.is_valid(): return self.error(f"Invalid problem format, error is {serializer.errors}") else: problem_info = serializer.data for item in problem_info["template"].keys(): if item not in SysOptions.language_names: return self.error(f"Unsupported language {item}") problem_info["display_id"] = problem_info["display_id"][:24] for k, v in problem_info["template"].items(): problem_info["template"][k] = build_problem_template(v["prepend"], v["template"], v["append"]) spj = problem_info["spj"] is not None rule_type = problem_info["rule_type"] test_case_score = problem_info["test_case_score"] # process test case _, test_case_id = self.process_zip(tmp_file, spj=spj, dir=f"{i}/testcase/") problem_obj = Problem.objects.create(_id=problem_info["display_id"], title=problem_info["title"], description=problem_info["description"]["value"], input_description=problem_info["input_description"][ "value"], output_description=problem_info["output_description"][ "value"], hint=problem_info["hint"]["value"], test_case_score=test_case_score if test_case_score else [], time_limit=problem_info["time_limit"], memory_limit=problem_info["memory_limit"], samples=problem_info["samples"], template=problem_info["template"], rule_type=problem_info["rule_type"], source=problem_info["source"], spj=spj, spj_code=problem_info["spj"]["code"] if spj else None, spj_language=problem_info["spj"][ "language"] if spj else None, spj_version=rand_str(8) if spj else "", languages=SysOptions.language_names, created_by=request.user, visible=False, difficulty=Difficulty.MID, total_score=sum(item["score"] for item in test_case_score) if rule_type == ProblemRuleType.OI else 0, test_case_id=test_case_id ) for tag_name in problem_info["tags"]: tag_obj, _ = ProblemTag.objects.get_or_create(name=tag_name) problem_obj.tags.add(tag_obj) return self.success({"import_count": count})