mirror of
https://github.com/aljazceru/CTFd.git
synced 2025-12-18 14:34:21 +01:00
Add API searching and filtering (#1515)
* Works on #1318 * Adds searching and filtering to most of the bulk API endpoints * Adds documentation on the GET parameters used to conduct searches
This commit is contained in:
@@ -3,9 +3,12 @@ from typing import List
|
||||
from flask import request
|
||||
from flask_restx import Namespace, Resource
|
||||
|
||||
from CTFd.api.v1.helpers.models import build_model_filters
|
||||
from CTFd.api.v1.helpers.request import validate_args
|
||||
from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic
|
||||
from CTFd.api.v1.schemas import APIDetailedSuccessResponse, APIListSuccessResponse
|
||||
from CTFd.cache import clear_standings
|
||||
from CTFd.constants import RawEnum
|
||||
from CTFd.models import Awards, Users, db
|
||||
from CTFd.schemas.awards import AwardSchema
|
||||
from CTFd.utils.config import is_teams_mode
|
||||
@@ -35,6 +38,55 @@ awards_namespace.schema_model(
|
||||
|
||||
@awards_namespace.route("")
|
||||
class AwardList(Resource):
|
||||
@admins_only
|
||||
@awards_namespace.doc(
|
||||
description="Endpoint to list Award objects in bulk",
|
||||
responses={
|
||||
200: ("Success", "AwardListSuccessResponse"),
|
||||
400: (
|
||||
"An error occured processing the provided or stored data",
|
||||
"APISimpleErrorResponse",
|
||||
),
|
||||
},
|
||||
)
|
||||
@validate_args(
|
||||
{
|
||||
"user_id": (int, None),
|
||||
"team_id": (int, None),
|
||||
"type": (str, None),
|
||||
"value": (int, None),
|
||||
"category": (int, None),
|
||||
"icon": (int, None),
|
||||
"q": (str, None),
|
||||
"field": (
|
||||
RawEnum(
|
||||
"AwardFields",
|
||||
{
|
||||
"name": "name",
|
||||
"description": "description",
|
||||
"category": "category",
|
||||
"icon": "icon",
|
||||
},
|
||||
),
|
||||
None,
|
||||
),
|
||||
},
|
||||
location="query",
|
||||
)
|
||||
def get(self, query_args):
|
||||
q = query_args.pop("q", None)
|
||||
field = str(query_args.pop("field", None))
|
||||
filters = build_model_filters(model=Awards, query=q, field=field)
|
||||
|
||||
awards = Awards.query.filter_by(**query_args).filter(*filters).all()
|
||||
schema = AwardSchema(many=True)
|
||||
response = schema.dump(awards)
|
||||
|
||||
if response.errors:
|
||||
return {"success": False, "errors": response.errors}, 400
|
||||
|
||||
return {"success": True, "data": response.data}
|
||||
|
||||
@admins_only
|
||||
@awards_namespace.doc(
|
||||
description="Endpoint to create an Award object",
|
||||
|
||||
@@ -5,9 +5,12 @@ from flask import abort, render_template, request, url_for
|
||||
from flask_restx import Namespace, Resource
|
||||
from sqlalchemy.sql import and_
|
||||
|
||||
from CTFd.api.v1.helpers.models import build_model_filters
|
||||
from CTFd.api.v1.helpers.request import validate_args
|
||||
from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic
|
||||
from CTFd.api.v1.schemas import APIDetailedSuccessResponse, APIListSuccessResponse
|
||||
from CTFd.cache import clear_standings
|
||||
from CTFd.constants import RawEnum
|
||||
from CTFd.models import ChallengeFiles as ChallengeFilesModel
|
||||
from CTFd.models import (
|
||||
Challenges,
|
||||
@@ -86,19 +89,56 @@ class ChallengeList(Resource):
|
||||
),
|
||||
},
|
||||
)
|
||||
def get(self):
|
||||
@validate_args(
|
||||
{
|
||||
"name": (str, None),
|
||||
"max_attempts": (int, None),
|
||||
"value": (int, None),
|
||||
"category": (str, None),
|
||||
"type": (str, None),
|
||||
"state": (str, None),
|
||||
"q": (str, None),
|
||||
"field": (
|
||||
RawEnum(
|
||||
"ChallengeFields",
|
||||
{
|
||||
"name": "name",
|
||||
"description": "description",
|
||||
"category": "category",
|
||||
"type": "type",
|
||||
"state": "state",
|
||||
},
|
||||
),
|
||||
None,
|
||||
),
|
||||
},
|
||||
location="query",
|
||||
)
|
||||
def get(self, query_args):
|
||||
# Build filtering queries
|
||||
q = query_args.pop("q", None)
|
||||
field = str(query_args.pop("field", None))
|
||||
filters = build_model_filters(model=Challenges, query=q, field=field)
|
||||
|
||||
# This can return None (unauth) if visibility is set to public
|
||||
user = get_current_user()
|
||||
|
||||
# Admins can request to see everything
|
||||
if is_admin() and request.args.get("view") == "admin":
|
||||
challenges = Challenges.query.order_by(Challenges.value).all()
|
||||
challenges = (
|
||||
Challenges.query.filter_by(**query_args)
|
||||
.filter(*filters)
|
||||
.order_by(Challenges.value)
|
||||
.all()
|
||||
)
|
||||
solve_ids = set([challenge.id for challenge in challenges])
|
||||
else:
|
||||
challenges = (
|
||||
Challenges.query.filter(
|
||||
and_(Challenges.state != "hidden", Challenges.state != "locked")
|
||||
)
|
||||
.filter_by(**query_args)
|
||||
.filter(*filters)
|
||||
.order_by(Challenges.value)
|
||||
.all()
|
||||
)
|
||||
|
||||
@@ -3,9 +3,12 @@ from typing import List
|
||||
from flask import request
|
||||
from flask_restx import Namespace, Resource
|
||||
|
||||
from CTFd.api.v1.helpers.models import build_model_filters
|
||||
from CTFd.api.v1.helpers.request import validate_args
|
||||
from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic
|
||||
from CTFd.api.v1.schemas import APIDetailedSuccessResponse, APIListSuccessResponse
|
||||
from CTFd.cache import clear_config, clear_standings
|
||||
from CTFd.constants import RawEnum
|
||||
from CTFd.models import Configs, db
|
||||
from CTFd.schemas.config import ConfigSchema
|
||||
from CTFd.utils import get_config, set_config
|
||||
@@ -46,8 +49,21 @@ class ConfigList(Resource):
|
||||
),
|
||||
},
|
||||
)
|
||||
def get(self):
|
||||
configs = Configs.query.all()
|
||||
@validate_args(
|
||||
{
|
||||
"key": (str, None),
|
||||
"value": (str, None),
|
||||
"q": (str, None),
|
||||
"field": (RawEnum("ConfigFields", {"key": "key", "value": "value"}), None),
|
||||
},
|
||||
location="query",
|
||||
)
|
||||
def get(self, query_args):
|
||||
q = query_args.pop("q", None)
|
||||
field = str(query_args.pop("field", None))
|
||||
filters = build_model_filters(model=Configs, query=q, field=field)
|
||||
|
||||
configs = Configs.query.filter_by(**query_args).filter(*filters).all()
|
||||
schema = ConfigSchema(many=True)
|
||||
response = schema.dump(configs)
|
||||
if response.errors:
|
||||
|
||||
@@ -3,8 +3,11 @@ from typing import List
|
||||
from flask import request
|
||||
from flask_restx import Namespace, Resource
|
||||
|
||||
from CTFd.api.v1.helpers.models import build_model_filters
|
||||
from CTFd.api.v1.helpers.request import validate_args
|
||||
from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic
|
||||
from CTFd.api.v1.schemas import APIDetailedSuccessResponse, APIListSuccessResponse
|
||||
from CTFd.constants import RawEnum
|
||||
from CTFd.models import Files, db
|
||||
from CTFd.schemas.files import FileSchema
|
||||
from CTFd.utils import uploads
|
||||
@@ -45,9 +48,24 @@ class FilesList(Resource):
|
||||
),
|
||||
},
|
||||
)
|
||||
def get(self):
|
||||
file_type = request.args.get("type")
|
||||
files = Files.query.filter_by(type=file_type).all()
|
||||
@validate_args(
|
||||
{
|
||||
"type": (str, None),
|
||||
"location": (str, None),
|
||||
"q": (str, None),
|
||||
"field": (
|
||||
RawEnum("FileFields", {"type": "type", "location": "location"}),
|
||||
None,
|
||||
),
|
||||
},
|
||||
location="query",
|
||||
)
|
||||
def get(self, query_args):
|
||||
q = query_args.pop("q", None)
|
||||
field = str(query_args.pop("field", None))
|
||||
filters = build_model_filters(model=Files, query=q, field=field)
|
||||
|
||||
files = Files.query.filter_by(**query_args).filter(*filters).all()
|
||||
schema = FileSchema(many=True)
|
||||
response = schema.dump(files)
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@ from typing import List
|
||||
from flask import request
|
||||
from flask_restx import Namespace, Resource
|
||||
|
||||
from CTFd.api.v1.helpers.models import build_model_filters
|
||||
from CTFd.api.v1.helpers.request import validate_args
|
||||
from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic
|
||||
from CTFd.api.v1.schemas import APIDetailedSuccessResponse, APIListSuccessResponse
|
||||
from CTFd.constants import RawEnum
|
||||
from CTFd.models import Flags, db
|
||||
from CTFd.plugins.flags import FLAG_CLASSES, get_flag_class
|
||||
from CTFd.schemas.flags import FlagSchema
|
||||
@@ -45,8 +48,28 @@ class FlagList(Resource):
|
||||
),
|
||||
},
|
||||
)
|
||||
def get(self):
|
||||
flags = Flags.query.all()
|
||||
@validate_args(
|
||||
{
|
||||
"challenge_id": (int, None),
|
||||
"type": (str, None),
|
||||
"content": (str, None),
|
||||
"data": (str, None),
|
||||
"q": (str, None),
|
||||
"field": (
|
||||
RawEnum(
|
||||
"FlagFields", {"type": "type", "content": "content", "data": "data"}
|
||||
),
|
||||
None,
|
||||
),
|
||||
},
|
||||
location="query",
|
||||
)
|
||||
def get(self, query_args):
|
||||
q = query_args.pop("q", None)
|
||||
field = str(query_args.pop("field", None))
|
||||
filters = build_model_filters(model=Flags, query=q, field=field)
|
||||
|
||||
flags = Flags.query.filter_by(**query_args).filter(*filters).all()
|
||||
schema = FlagSchema(many=True)
|
||||
response = schema.dump(flags)
|
||||
if response.errors:
|
||||
|
||||
7
CTFd/api/v1/helpers/models.py
Normal file
7
CTFd/api/v1/helpers/models.py
Normal file
@@ -0,0 +1,7 @@
|
||||
def build_model_filters(model, query, field):
|
||||
filters = []
|
||||
if query:
|
||||
# The field exists as an exposed column
|
||||
if model.__mapper__.has_property(field):
|
||||
filters.append(getattr(model, field).like("%{}%".format(query)))
|
||||
return filters
|
||||
@@ -3,8 +3,11 @@ from typing import List
|
||||
from flask import request
|
||||
from flask_restx import Namespace, Resource
|
||||
|
||||
from CTFd.api.v1.helpers.models import build_model_filters
|
||||
from CTFd.api.v1.helpers.request import validate_args
|
||||
from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic
|
||||
from CTFd.api.v1.schemas import APIDetailedSuccessResponse, APIListSuccessResponse
|
||||
from CTFd.constants import RawEnum
|
||||
from CTFd.models import Hints, HintUnlocks, db
|
||||
from CTFd.schemas.hints import HintSchema
|
||||
from CTFd.utils.decorators import admins_only, authed_only, during_ctf_time_only
|
||||
@@ -45,8 +48,26 @@ class HintList(Resource):
|
||||
),
|
||||
},
|
||||
)
|
||||
def get(self):
|
||||
hints = Hints.query.all()
|
||||
@validate_args(
|
||||
{
|
||||
"type": (str, None),
|
||||
"challenge_id": (int, None),
|
||||
"content": (str, None),
|
||||
"cost": (int, None),
|
||||
"q": (str, None),
|
||||
"field": (
|
||||
RawEnum("HintFields", {"type": "type", "content": "content"}),
|
||||
None,
|
||||
),
|
||||
},
|
||||
location="query",
|
||||
)
|
||||
def get(self, query_args):
|
||||
q = query_args.pop("q", None)
|
||||
field = str(query_args.pop("field", None))
|
||||
filters = build_model_filters(model=Hints, query=q, field=field)
|
||||
|
||||
hints = Hints.query.filter_by(**query_args).filter(*filters).all()
|
||||
response = HintSchema(many=True).dump(hints)
|
||||
|
||||
if response.errors:
|
||||
|
||||
@@ -3,8 +3,11 @@ from typing import List
|
||||
from flask import current_app, request
|
||||
from flask_restx import Namespace, Resource
|
||||
|
||||
from CTFd.api.v1.helpers.models import build_model_filters
|
||||
from CTFd.api.v1.helpers.request import validate_args
|
||||
from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic
|
||||
from CTFd.api.v1.schemas import APIDetailedSuccessResponse, APIListSuccessResponse
|
||||
from CTFd.constants import RawEnum
|
||||
from CTFd.models import Notifications, db
|
||||
from CTFd.schemas.notifications import NotificationSchema
|
||||
from CTFd.utils.decorators import admins_only
|
||||
@@ -46,8 +49,28 @@ class NotificantionList(Resource):
|
||||
),
|
||||
},
|
||||
)
|
||||
def get(self):
|
||||
notifications = Notifications.query.all()
|
||||
@validate_args(
|
||||
{
|
||||
"title": (str, None),
|
||||
"content": (str, None),
|
||||
"user_id": (int, None),
|
||||
"team_id": (int, None),
|
||||
"q": (str, None),
|
||||
"field": (
|
||||
RawEnum("NotificationFields", {"title": "title", "content": "content"}),
|
||||
None,
|
||||
),
|
||||
},
|
||||
location="query",
|
||||
)
|
||||
def get(self, query_args):
|
||||
q = query_args.pop("q", None)
|
||||
field = str(query_args.pop("field", None))
|
||||
filters = build_model_filters(model=Notifications, query=q, field=field)
|
||||
|
||||
notifications = (
|
||||
Notifications.query.filter_by(**query_args).filter(*filters).all()
|
||||
)
|
||||
schema = NotificationSchema(many=True)
|
||||
result = schema.dump(notifications)
|
||||
if result.errors:
|
||||
|
||||
@@ -3,10 +3,12 @@ from typing import List
|
||||
from flask import request
|
||||
from flask_restx import Namespace, Resource
|
||||
|
||||
from CTFd.api.v1.helpers.models import build_model_filters
|
||||
from CTFd.api.v1.helpers.request import validate_args
|
||||
from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic
|
||||
from CTFd.api.v1.schemas import APIDetailedSuccessResponse, APIListSuccessResponse
|
||||
from CTFd.cache import clear_pages
|
||||
from CTFd.constants import RawEnum
|
||||
from CTFd.models import Pages, db
|
||||
from CTFd.schemas.pages import PageSchema
|
||||
from CTFd.utils.decorators import admins_only
|
||||
@@ -59,11 +61,23 @@ class PageList(Resource):
|
||||
"draft": (bool, None),
|
||||
"hidden": (bool, None),
|
||||
"auth_required": (bool, None),
|
||||
"q": (str, None),
|
||||
"field": (
|
||||
RawEnum(
|
||||
"PageFields",
|
||||
{"title": "title", "route": "route", "content": "content"},
|
||||
),
|
||||
None,
|
||||
),
|
||||
},
|
||||
location="query",
|
||||
)
|
||||
def get(self, query):
|
||||
pages = Pages.query.filter_by(**query).all()
|
||||
def get(self, query_args):
|
||||
q = query_args.pop("q", None)
|
||||
field = str(query_args.pop("field", None))
|
||||
filters = build_model_filters(model=Pages, query=q, field=field)
|
||||
|
||||
pages = Pages.query.filter_by(**query_args).filter(*filters).all()
|
||||
schema = PageSchema(exclude=["content"], many=True)
|
||||
response = schema.dump(pages)
|
||||
if response.errors:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from typing import List
|
||||
|
||||
from flask import request
|
||||
from flask_restx import Namespace, Resource
|
||||
|
||||
from CTFd.api.v1.helpers.models import build_model_filters
|
||||
from CTFd.api.v1.helpers.request import validate_args
|
||||
from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic
|
||||
from CTFd.api.v1.schemas import (
|
||||
@@ -10,6 +10,7 @@ from CTFd.api.v1.schemas import (
|
||||
PaginatedAPIListSuccessResponse,
|
||||
)
|
||||
from CTFd.cache import clear_standings
|
||||
from CTFd.constants import RawEnum
|
||||
from CTFd.models import Submissions, db
|
||||
from CTFd.schemas.submissions import SubmissionSchema
|
||||
from CTFd.utils.decorators import admins_only
|
||||
@@ -52,21 +53,45 @@ class SubmissionsList(Resource):
|
||||
),
|
||||
},
|
||||
)
|
||||
def get(self):
|
||||
args = request.args.to_dict()
|
||||
@validate_args(
|
||||
{
|
||||
"challenge_id": (int, None),
|
||||
"user_id": (int, None),
|
||||
"team_id": (int, None),
|
||||
"ip": (str, None),
|
||||
"provided": (str, None),
|
||||
"type": (str, None),
|
||||
"q": (str, None),
|
||||
"field": (
|
||||
RawEnum(
|
||||
"SubmissionFields",
|
||||
{
|
||||
"challenge_id": "challenge_id",
|
||||
"user_id": "user_id",
|
||||
"team_id": "team_id",
|
||||
"ip": "ip",
|
||||
"provided": "provided",
|
||||
"type": "type",
|
||||
},
|
||||
),
|
||||
None,
|
||||
),
|
||||
},
|
||||
location="query",
|
||||
)
|
||||
def get(self, query_args):
|
||||
q = query_args.pop("q", None)
|
||||
field = str(query_args.pop("field", None))
|
||||
filters = build_model_filters(model=Submissions, query=q, field=field)
|
||||
|
||||
args = query_args
|
||||
schema = SubmissionSchema(many=True)
|
||||
pagination_args = {
|
||||
"per_page": int(args.pop("per_page", 50)),
|
||||
"page": int(args.pop("page", 1)),
|
||||
}
|
||||
if args:
|
||||
submissions = Submissions.query.filter_by(**args).paginate(
|
||||
**pagination_args, max_per_page=100
|
||||
)
|
||||
else:
|
||||
submissions = Submissions.query.paginate(
|
||||
**pagination_args, max_per_page=100
|
||||
)
|
||||
|
||||
submissions = (
|
||||
Submissions.query.filter_by(**args)
|
||||
.filter(*filters)
|
||||
.paginate(max_per_page=100)
|
||||
)
|
||||
|
||||
response = schema.dump(submissions.items)
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@ from typing import List
|
||||
from flask import request
|
||||
from flask_restx import Namespace, Resource
|
||||
|
||||
from CTFd.api.v1.helpers.models import build_model_filters
|
||||
from CTFd.api.v1.helpers.request import validate_args
|
||||
from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic
|
||||
from CTFd.api.v1.schemas import APIDetailedSuccessResponse, APIListSuccessResponse
|
||||
from CTFd.constants import RawEnum
|
||||
from CTFd.models import Tags, db
|
||||
from CTFd.schemas.tags import TagSchema
|
||||
from CTFd.utils.decorators import admins_only
|
||||
@@ -42,9 +45,26 @@ class TagList(Resource):
|
||||
),
|
||||
},
|
||||
)
|
||||
def get(self):
|
||||
# TODO: Filter by challenge_id
|
||||
tags = Tags.query.all()
|
||||
@validate_args(
|
||||
{
|
||||
"challenge_id": (int, None),
|
||||
"value": (str, None),
|
||||
"q": (str, None),
|
||||
"field": (
|
||||
RawEnum(
|
||||
"TagFields", {"challenge_id": "challenge_id", "value": "value"}
|
||||
),
|
||||
None,
|
||||
),
|
||||
},
|
||||
location="query",
|
||||
)
|
||||
def get(self, query_args):
|
||||
q = query_args.pop("q", None)
|
||||
field = str(query_args.pop("field", None))
|
||||
filters = build_model_filters(model=Tags, query=q, field=field)
|
||||
|
||||
tags = Tags.query.filter_by(**query_args).filter(*filters).all()
|
||||
schema = TagSchema(many=True)
|
||||
response = schema.dump(tags)
|
||||
|
||||
|
||||
@@ -4,12 +4,15 @@ from typing import List
|
||||
from flask import abort, request, session
|
||||
from flask_restx import Namespace, Resource
|
||||
|
||||
from CTFd.api.v1.helpers.models import build_model_filters
|
||||
from CTFd.api.v1.helpers.request import validate_args
|
||||
from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic
|
||||
from CTFd.api.v1.schemas import (
|
||||
APIDetailedSuccessResponse,
|
||||
PaginatedAPIListSuccessResponse,
|
||||
)
|
||||
from CTFd.cache import clear_standings, clear_team_session, clear_user_session
|
||||
from CTFd.constants import RawEnum
|
||||
from CTFd.models import Awards, Submissions, Teams, Unlocks, Users, db
|
||||
from CTFd.schemas.awards import AwardSchema
|
||||
from CTFd.schemas.submissions import SubmissionSchema
|
||||
@@ -57,12 +60,44 @@ class TeamList(Resource):
|
||||
),
|
||||
},
|
||||
)
|
||||
def get(self):
|
||||
@validate_args(
|
||||
{
|
||||
"affiliation": (str, None),
|
||||
"country": (str, None),
|
||||
"bracket": (str, None),
|
||||
"q": (str, None),
|
||||
"field": (
|
||||
RawEnum(
|
||||
"TeamFields",
|
||||
{
|
||||
"name": "name",
|
||||
"website": "website",
|
||||
"country": "country",
|
||||
"bracket": "bracket",
|
||||
"affiliation": "affiliation",
|
||||
},
|
||||
),
|
||||
None,
|
||||
),
|
||||
},
|
||||
location="query",
|
||||
)
|
||||
def get(self, query_args):
|
||||
q = query_args.pop("q", None)
|
||||
field = str(query_args.pop("field", None))
|
||||
filters = build_model_filters(model=Teams, query=q, field=field)
|
||||
|
||||
if is_admin() and request.args.get("view") == "admin":
|
||||
teams = Teams.query.filter_by().paginate(per_page=50, max_per_page=100)
|
||||
teams = (
|
||||
Teams.query.filter_by(**query_args)
|
||||
.filter(*filters)
|
||||
.paginate(per_page=50, max_per_page=100)
|
||||
)
|
||||
else:
|
||||
teams = Teams.query.filter_by(hidden=False, banned=False).paginate(
|
||||
per_page=50, max_per_page=100
|
||||
teams = (
|
||||
Teams.query.filter_by(hidden=False, banned=False, **query_args)
|
||||
.filter(*filters)
|
||||
.paginate(per_page=50, max_per_page=100)
|
||||
)
|
||||
|
||||
user_type = get_current_user_type(fallback="user")
|
||||
|
||||
@@ -3,9 +3,12 @@ from typing import List
|
||||
from flask import request
|
||||
from flask_restx import Namespace, Resource
|
||||
|
||||
from CTFd.api.v1.helpers.models import build_model_filters
|
||||
from CTFd.api.v1.helpers.request import validate_args
|
||||
from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic
|
||||
from CTFd.api.v1.schemas import APIDetailedSuccessResponse, APIListSuccessResponse
|
||||
from CTFd.cache import clear_standings
|
||||
from CTFd.constants import RawEnum
|
||||
from CTFd.models import Unlocks, db, get_class_by_tablename
|
||||
from CTFd.schemas.awards import AwardSchema
|
||||
from CTFd.schemas.unlocks import UnlockSchema
|
||||
@@ -53,10 +56,28 @@ class UnlockList(Resource):
|
||||
),
|
||||
},
|
||||
)
|
||||
def get(self):
|
||||
hints = Unlocks.query.all()
|
||||
@validate_args(
|
||||
{
|
||||
"user_id": (int, None),
|
||||
"team_id": (int, None),
|
||||
"target": (int, None),
|
||||
"type": (str, None),
|
||||
"q": (str, None),
|
||||
"field": (
|
||||
RawEnum("UnlockFields", {"target": "target", "type": "type"}),
|
||||
None,
|
||||
),
|
||||
},
|
||||
location="query",
|
||||
)
|
||||
def get(self, query_args):
|
||||
q = query_args.pop("q", None)
|
||||
field = str(query_args.pop("field", None))
|
||||
filters = build_model_filters(model=Unlocks, query=q, field=field)
|
||||
|
||||
unlocks = Unlocks.query.filter_by(**query_args).filter(*filters).all()
|
||||
schema = UnlockSchema()
|
||||
response = schema.dump(hints)
|
||||
response = schema.dump(unlocks)
|
||||
|
||||
if response.errors:
|
||||
return {"success": False, "errors": response.errors}, 400
|
||||
|
||||
@@ -3,12 +3,15 @@ from typing import List
|
||||
from flask import abort, request
|
||||
from flask_restx import Namespace, Resource
|
||||
|
||||
from CTFd.api.v1.helpers.models import build_model_filters
|
||||
from CTFd.api.v1.helpers.request import validate_args
|
||||
from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic
|
||||
from CTFd.api.v1.schemas import (
|
||||
APIDetailedSuccessResponse,
|
||||
PaginatedAPIListSuccessResponse,
|
||||
)
|
||||
from CTFd.cache import clear_standings, clear_user_session
|
||||
from CTFd.constants import RawEnum
|
||||
from CTFd.models import (
|
||||
Awards,
|
||||
Notifications,
|
||||
@@ -69,12 +72,44 @@ class UserList(Resource):
|
||||
),
|
||||
},
|
||||
)
|
||||
def get(self):
|
||||
@validate_args(
|
||||
{
|
||||
"affiliation": (str, None),
|
||||
"country": (str, None),
|
||||
"bracket": (str, None),
|
||||
"q": (str, None),
|
||||
"field": (
|
||||
RawEnum(
|
||||
"UserFields",
|
||||
{
|
||||
"name": "name",
|
||||
"website": "website",
|
||||
"country": "country",
|
||||
"bracket": "bracket",
|
||||
"affiliation": "affiliation",
|
||||
},
|
||||
),
|
||||
None,
|
||||
),
|
||||
},
|
||||
location="query",
|
||||
)
|
||||
def get(self, query_args):
|
||||
q = query_args.pop("q", None)
|
||||
field = str(query_args.pop("field", None))
|
||||
filters = build_model_filters(model=Users, query=q, field=field)
|
||||
|
||||
if is_admin() and request.args.get("view") == "admin":
|
||||
users = Users.query.filter_by().paginate(per_page=50, max_per_page=100)
|
||||
users = (
|
||||
Users.query.filter_by(**query_args)
|
||||
.filter(*filters)
|
||||
.paginate(per_page=50, max_per_page=100)
|
||||
)
|
||||
else:
|
||||
users = Users.query.filter_by(banned=False, hidden=False).paginate(
|
||||
per_page=50, max_per_page=100
|
||||
users = (
|
||||
Users.query.filter_by(banned=False, hidden=False, **query_args)
|
||||
.filter(*filters)
|
||||
.paginate(per_page=50, max_per_page=100)
|
||||
)
|
||||
|
||||
response = UserSchema(view="user", many=True).dump(users.items)
|
||||
|
||||
Reference in New Issue
Block a user