API access with authorization #475

This commit is contained in:
Daniel Grams 2023-05-21 19:05:28 +02:00
parent b874550a64
commit fb34d4323c
58 changed files with 556 additions and 358 deletions

View File

@ -29,3 +29,4 @@ JWT_PRIVATE_KEY=""
JWT_PUBLIC_JWKS=''
DOCS_URL=''
ADMIN_UNIT_CREATE_REQUIRES_ADMIN=False
API_READ_ANONYM=False

View File

@ -26,6 +26,7 @@ x-web-env:
DOCS_URL: ${DOCS_URL}
SITE_NAME: ${SITE_NAME}
ADMIN_UNIT_CREATE_REQUIRES_ADMIN: ${ADMIN_UNIT_CREATE_REQUIRES_ADMIN:-False}
API_READ_ANONYM: ${API_READ_ANONYM:-False}
x-web:
&default-web

View File

@ -52,6 +52,7 @@ app.config["SEO_SITEMAP_PING_GOOGLE"] = getenv_bool("SEO_SITEMAP_PING_GOOGLE", "
app.config["GOOGLE_MAPS_API_KEY"] = os.getenv("GOOGLE_MAPS_API_KEY")
set_env_to_app(app, "SITE_NAME", "EventCally")
app.config["FLASK_DEBUG"] = getenv_bool("FLASK_DEBUG", "False")
app.config["API_READ_ANONYM"] = getenv_bool("API_READ_ANONYM", "False")
# if app.config["FLASK_DEBUG"]:
# logging.basicConfig(level=logging.DEBUG)
@ -268,6 +269,7 @@ from project.views import (
admin_unit,
admin_unit_member,
admin_unit_member_invitation,
custom_widget,
dump,
event,
event_date,

View File

@ -34,6 +34,7 @@ def owner_access_or_401(user_id):
def login_api_user() -> bool:
return (
current_token
and current_token.user
and login_user(current_token.user)
or current_user
and current_user.is_authenticated

View File

@ -1,4 +1,4 @@
from apispec import APISpec
from apispec import APISpec, BasePlugin
from apispec.exceptions import DuplicateComponentNameError
from apispec.ext.marshmallow import MarshmallowPlugin
from flask import url_for
@ -115,6 +115,11 @@ class RestApi(Api):
data["errors"] = errors
class DocSecurityPlugin(BasePlugin):
def operation_helper(self, path=None, operations=None, **kwargs):
pass
scope_list = [
"openid",
"profile",
@ -138,7 +143,7 @@ app.config.update(
"APISPEC_SPEC": APISpec(
title="Event calendar API",
version="0.1.0",
plugins=[marshmallow_plugin],
plugins=[marshmallow_plugin, DocSecurityPlugin()],
openapi_version="2.0",
info=dict(
description="This API provides endpoints to interact with the event calendar data."
@ -174,16 +179,33 @@ def add_oauth2_scheme_with_transport(insecure: bool):
authorizationUrl = url_for("authorize", _external=True, _scheme="https")
tokenUrl = url_for("issue_token", _external=True, _scheme="https")
oauth2_scheme = {
scopes = {k: k for _, k in enumerate(scope_list)}
oauth2_authorization_code_scheme = {
"type": "oauth2",
"authorizationUrl": authorizationUrl,
"tokenUrl": tokenUrl,
"flow": "accessCode",
"scopes": {k: k for _, k in enumerate(scope_list)},
"scopes": scopes,
}
try:
api_docs.spec.components.security_scheme("oauth2", oauth2_scheme)
api_docs.spec.components.security_scheme(
"oauth2AuthCode", oauth2_authorization_code_scheme
)
except DuplicateComponentNameError: # pragma: no cover
pass
oauth2_client_credentials_scheme = {
"type": "oauth2",
"tokenUrl": tokenUrl,
"flow": "application",
"scopes": scopes,
}
try:
api_docs.spec.components.security_scheme(
"oauth2ClientCredentials", oauth2_client_credentials_scheme
)
except DuplicateComponentNameError: # pragma: no cover
pass

View File

@ -16,13 +16,13 @@ from project.models import CustomWidget
class CustomWidgetResource(BaseResource):
@doc(summary="Get custom widget", tags=["Custom Widgets"])
@marshal_with(CustomWidgetSchema)
@require_api_access()
def get(self, id):
return CustomWidget.query.get_or_404(id)
@doc(
summary="Update custom widget",
tags=["Custom Widgets"],
security=[{"oauth2": ["customwidget:write"]}],
)
@use_kwargs(CustomWidgetPostRequestSchema, location="json", apply=False)
@marshal_with(None, 204)
@ -42,7 +42,6 @@ class CustomWidgetResource(BaseResource):
@doc(
summary="Patch custom widget",
tags=["Custom Widgets"],
security=[{"oauth2": ["customwidget:write"]}],
)
@use_kwargs(CustomWidgetPatchRequestSchema, location="json", apply=False)
@marshal_with(None, 204)
@ -62,7 +61,6 @@ class CustomWidgetResource(BaseResource):
@doc(
summary="Delete custom widget",
tags=["Custom Widgets"],
security=[{"oauth2": ["customwidget:write"]}],
)
@marshal_with(None, 204)
@require_api_access("customwidget:write")

View File

@ -2,7 +2,7 @@ from flask_apispec import doc, marshal_with
from project.api import add_api_resource
from project.api.dump.schemas import DumpResponseSchema
from project.api.resources import BaseResource
from project.api.resources import BaseResource, require_api_access
from project.api.schemas import NoneSchema
@ -14,6 +14,7 @@ class DumpResource(BaseResource):
)
@marshal_with(NoneSchema, 404)
@marshal_with(DumpResponseSchema, 200)
@require_api_access()
def get(self, **kwargs):
return None, 404

View File

@ -26,10 +26,9 @@ from project.api.event_date.schemas import (
EventDateListRequestSchema,
EventDateListResponseSchema,
)
from project.api.resources import BaseResource
from project.api.resources import BaseResource, require_api_access
from project.api.schemas import NoneSchema
from project.models import AdminUnit, Event, EventDate, PublicStatus
from project.oauth2 import require_oauth
from project.services.event import (
get_event_with_details_or_404,
get_events_query,
@ -61,6 +60,7 @@ class EventListResource(BaseResource):
@doc(summary="List events", tags=["Events"])
@use_kwargs(EventListRequestSchema, location=("query"))
@marshal_with(EventListResponseSchema)
@require_api_access()
def get(self, **kwargs):
pagination = (
Event.query.join(Event.admin_unit)
@ -78,7 +78,7 @@ class EventListResource(BaseResource):
class EventResource(BaseResource):
@doc(summary="Get event", tags=["Events"])
@marshal_with(EventSchema)
@require_oauth(optional=True)
@require_api_access()
def get(self, id):
login_api_user()
event = get_event_with_details_or_404(id)
@ -86,11 +86,12 @@ class EventResource(BaseResource):
return event
@doc(
summary="Update event", tags=["Events"], security=[{"oauth2": ["event:write"]}]
summary="Update event",
tags=["Events"],
)
@use_kwargs(EventPostRequestSchema, location="json", apply=False)
@marshal_with(None, 204)
@require_oauth("event:write")
@require_api_access("event:write")
def put(self, id):
login_api_user_or_401()
event = Event.query.get_or_404(id)
@ -106,10 +107,13 @@ class EventResource(BaseResource):
return make_response("", 204)
@doc(summary="Patch event", tags=["Events"], security=[{"oauth2": ["event:write"]}])
@doc(
summary="Patch event",
tags=["Events"],
)
@use_kwargs(EventPatchRequestSchema, location="json", apply=False)
@marshal_with(None, 204)
@require_oauth("event:write")
@require_api_access("event:write")
def patch(self, id):
login_api_user_or_401()
event = Event.query.get_or_404(id)
@ -126,10 +130,11 @@ class EventResource(BaseResource):
return make_response("", 204)
@doc(
summary="Delete event", tags=["Events"], security=[{"oauth2": ["event:write"]}]
summary="Delete event",
tags=["Events"],
)
@marshal_with(None, 204)
@require_oauth("event:write")
@require_api_access("event:write")
def delete(self, id):
login_api_user_or_401()
event = Event.query.get_or_404(id)
@ -145,7 +150,7 @@ class EventDatesResource(BaseResource):
@doc(summary="List dates for event", tags=["Events", "Event Dates"])
@use_kwargs(EventDateListRequestSchema, location=("query"))
@marshal_with(EventDateListResponseSchema)
@require_oauth(optional=True)
@require_api_access()
def get(self, id, **kwargs):
event = Event.query.options(
load_only(Event.id, Event.public_status)
@ -164,7 +169,7 @@ class EventSearchResource(BaseResource):
@doc(summary="Search for events", tags=["Events"])
@use_kwargs(EventSearchRequestSchema, location=("query"))
@marshal_with(EventSearchResponseSchema)
@require_oauth(optional=True)
@require_api_access()
def get(self, **kwargs):
login_api_user()
params = EventSearchParams()
@ -177,6 +182,7 @@ class EventReportsResource(BaseResource):
@doc(summary="Add event report", tags=["Events"])
@use_kwargs(EventReportPostSchema, location="json", apply=False)
@marshal_with(NoneSchema, 204)
@require_api_access()
def post(self, id):
event = Event.query.options(
load_only(Event.id, Event.public_status)

View File

@ -5,7 +5,7 @@ from project.api.event_category.schemas import (
EventCategoryListRequestSchema,
EventCategoryListResponseSchema,
)
from project.api.resources import BaseResource
from project.api.resources import BaseResource, require_api_access
from project.models import EventCategory
@ -13,6 +13,7 @@ class EventCategoryListResource(BaseResource):
@doc(summary="List event categories", tags=["Event Categories"])
@use_kwargs(EventCategoryListRequestSchema, location=("query"))
@marshal_with(EventCategoryListResponseSchema)
@require_api_access()
def get(self, **kwargs):
pagination = EventCategory.query.paginate()
return pagination

View File

@ -12,9 +12,8 @@ from project.api.event_date.schemas import (
EventDateSearchRequestSchema,
EventDateSearchResponseSchema,
)
from project.api.resources import BaseResource
from project.api.resources import BaseResource, require_api_access
from project.models import AdminUnit, Event, EventDate, PublicStatus
from project.oauth2 import require_oauth
from project.services.event import get_event_dates_query
from project.services.event_search import EventSearchParams
@ -23,6 +22,7 @@ class EventDateListResource(BaseResource):
@doc(summary="List event dates", tags=["Event Dates"])
@use_kwargs(EventDateListRequestSchema, location=("query"))
@marshal_with(EventDateListResponseSchema)
@require_api_access()
def get(self, **kwargs):
pagination = (
EventDate.query.join(EventDate.event)
@ -42,7 +42,7 @@ class EventDateListResource(BaseResource):
class EventDateResource(BaseResource):
@doc(summary="Get event date", tags=["Event Dates"])
@marshal_with(EventDateSchema)
@require_oauth(optional=True)
@require_api_access()
def get(self, id):
event_date = EventDate.query.options(
defaultload(EventDate.event).load_only(
@ -57,7 +57,7 @@ class EventDateSearchResource(BaseResource):
@doc(summary="Search for event dates", tags=["Event Dates"])
@use_kwargs(EventDateSearchRequestSchema, location=("query"))
@marshal_with(EventDateSearchResponseSchema)
@require_oauth(optional=True)
@require_api_access()
def get(self, **kwargs):
login_api_user()
params = EventSearchParams()

View File

@ -19,13 +19,13 @@ from project.services.event_search import EventSearchParams
class EventListModelResource(BaseResource):
@doc(summary="Get event list", tags=["Event Lists"])
@marshal_with(EventListSchema)
@require_api_access()
def get(self, id):
return EventList.query.get_or_404(id)
@doc(
summary="Update event list",
tags=["Event Lists"],
security=[{"oauth2": ["eventlist:write"]}],
)
@use_kwargs(EventListUpdateRequestSchema, location="json", apply=False)
@marshal_with(None, 204)
@ -45,7 +45,6 @@ class EventListModelResource(BaseResource):
@doc(
summary="Patch event list",
tags=["Event Lists"],
security=[{"oauth2": ["eventlist:write"]}],
)
@use_kwargs(EventListPatchRequestSchema, location="json", apply=False)
@marshal_with(None, 204)
@ -65,7 +64,6 @@ class EventListModelResource(BaseResource):
@doc(
summary="Delete event list",
tags=["Event Lists"],
security=[{"oauth2": ["eventlist:write"]}],
)
@marshal_with(None, 204)
@require_api_access("eventlist:write")
@ -87,6 +85,7 @@ class EventListEventListResource(BaseResource):
)
@use_kwargs(EventListRequestSchema, location=("query"))
@marshal_with(EventListResponseSchema)
@require_api_access()
def get(self, id, **kwargs):
params = EventSearchParams()
params.event_list_id = id
@ -98,7 +97,6 @@ class EventListEventListWriteResource(BaseResource):
@doc(
summary="Add event",
tags=["Event Lists", "Events"],
security=[{"oauth2": ["eventlist:write"]}],
)
@marshal_with(None, 204)
@require_api_access("eventlist:write")
@ -120,7 +118,6 @@ class EventListEventListWriteResource(BaseResource):
@doc(
summary="Remove event",
tags=["Event Lists", "Events"],
security=[{"oauth2": ["eventlist:write"]}],
)
@marshal_with(None, 204)
@require_api_access("eventlist:write")

View File

@ -2,13 +2,14 @@ from flask_apispec import doc, marshal_with
from project.api import add_api_resource
from project.api.event_reference.schemas import EventReferenceSchema
from project.api.resources import BaseResource
from project.api.resources import BaseResource, require_api_access
from project.models import EventReference
class EventReferenceResource(BaseResource):
@doc(summary="Get event reference", tags=["Event References"])
@marshal_with(EventReferenceSchema)
@require_api_access()
def get(self, id):
return EventReference.query.get_or_404(id)

View File

@ -74,7 +74,6 @@ from project.api.place.schemas import (
)
from project.api.resources import BaseResource, require_api_access
from project.models import AdminUnit, Event, PublicStatus
from project.oauth2 import require_oauth
from project.services.admin_unit import (
get_admin_unit_invitation_query,
get_admin_unit_query,
@ -98,7 +97,7 @@ from project.views.utils import send_mail
class OrganizationResource(BaseResource):
@doc(summary="Get organization", tags=["Organizations"])
@marshal_with(OrganizationSchema)
@require_oauth(optional=True)
@require_api_access()
def get(self, id):
return AdminUnit.query.get_or_404(id)
@ -111,7 +110,7 @@ class OrganizationEventDateSearchResource(BaseResource):
)
@use_kwargs(EventDateSearchRequestSchema, location=("query"))
@marshal_with(EventDateSearchResponseSchema)
@require_oauth(optional=True)
@require_api_access()
def get(self, id, **kwargs):
admin_unit = AdminUnit.query.get_or_404(id)
@ -128,7 +127,7 @@ class OrganizationEventSearchResource(BaseResource):
@doc(summary="Search for events of organization", tags=["Organizations", "Events"])
@use_kwargs(EventSearchRequestSchema, location=("query"))
@marshal_with(EventSearchResponseSchema)
@require_oauth(optional=True)
@require_api_access()
def get(self, id, **kwargs):
admin_unit = AdminUnit.query.get_or_404(id)
@ -145,7 +144,7 @@ class OrganizationEventListResource(BaseResource):
@doc(summary="List events of organization", tags=["Organizations", "Events"])
@use_kwargs(EventListRequestSchema, location=("query"))
@marshal_with(EventListResponseSchema)
@require_oauth(optional=True)
@require_api_access()
def get(self, id, **kwargs):
admin_unit = AdminUnit.query.get_or_404(id)
@ -164,11 +163,10 @@ class OrganizationEventListResource(BaseResource):
@doc(
summary="Add new event",
tags=["Organizations", "Events"],
security=[{"oauth2": ["event:write"]}],
)
@use_kwargs(EventPostRequestSchema, location="json", apply=False)
@marshal_with(EventIdSchema, 201)
@require_oauth("event:write")
@require_api_access("event:write")
def post(self, id):
login_api_user_or_401()
admin_unit = get_admin_unit_for_manage_or_404(id)
@ -189,6 +187,7 @@ class OrganizationEventImportResource(BaseResource):
@marshal_with(EventIdSchema, 201)
@require_api_access("event:write")
def post(self, id, **kwargs):
login_api_user_or_401()
admin_unit = AdminUnit.query.get_or_404(id)
access_or_401(admin_unit, "event:create")
@ -211,10 +210,13 @@ class OrganizationEventImportResource(BaseResource):
class OrganizationListResource(BaseResource):
@doc(summary="List organizations", tags=["Organizations"])
@doc(
summary="List organizations",
tags=["Organizations"],
)
@use_kwargs(OrganizationListRequestSchema, location=("query"))
@marshal_with(OrganizationListResponseSchema)
@require_oauth(optional=True)
@require_api_access()
def get(self, **kwargs):
keyword = kwargs["keyword"] if "keyword" in kwargs else None
@ -231,6 +233,7 @@ class OrganizationOrganizerListResource(BaseResource):
)
@use_kwargs(OrganizerListRequestSchema, location=("query"))
@marshal_with(OrganizerListResponseSchema)
@require_api_access()
def get(self, id, **kwargs):
admin_unit = AdminUnit.query.get_or_404(id)
name = kwargs["name"] if "name" in kwargs else None
@ -241,11 +244,10 @@ class OrganizationOrganizerListResource(BaseResource):
@doc(
summary="Add new organizer",
tags=["Organizations", "Organizers"],
security=[{"oauth2": ["organizer:write"]}],
)
@use_kwargs(OrganizerPostRequestSchema, location="json", apply=False)
@marshal_with(OrganizerIdSchema, 201)
@require_oauth("organizer:write")
@require_api_access("organizer:write")
def post(self, id):
login_api_user_or_401()
admin_unit = get_admin_unit_for_manage_or_404(id)
@ -264,6 +266,7 @@ class OrganizationPlaceListResource(BaseResource):
@doc(summary="List places of organization", tags=["Organizations", "Places"])
@use_kwargs(PlaceListRequestSchema, location=("query"))
@marshal_with(PlaceListResponseSchema)
@require_api_access()
def get(self, id, **kwargs):
admin_unit = AdminUnit.query.get_or_404(id)
name = kwargs["name"] if "name" in kwargs else None
@ -274,11 +277,10 @@ class OrganizationPlaceListResource(BaseResource):
@doc(
summary="Add new place",
tags=["Organizations", "Places"],
security=[{"oauth2": ["place:write"]}],
)
@use_kwargs(PlacePostRequestSchema, location="json", apply=False)
@marshal_with(PlaceIdSchema, 201)
@require_oauth("place:write")
@require_api_access("place:write")
def post(self, id):
login_api_user_or_401()
admin_unit = get_admin_unit_for_manage_or_404(id)
@ -300,6 +302,7 @@ class OrganizationIncomingEventReferenceListResource(BaseResource):
)
@use_kwargs(EventReferenceListRequestSchema, location=("query"))
@marshal_with(EventReferenceListResponseSchema)
@require_api_access()
def get(self, id, **kwargs):
admin_unit = AdminUnit.query.get_or_404(id)
@ -314,6 +317,7 @@ class OrganizationOutgoingEventReferenceListResource(BaseResource):
)
@use_kwargs(EventReferenceListRequestSchema, location=("query"))
@marshal_with(EventReferenceListResponseSchema)
@require_api_access()
def get(self, id, **kwargs):
admin_unit = AdminUnit.query.get_or_404(id)
@ -325,7 +329,6 @@ class OrganizationOutgoingRelationListResource(BaseResource):
@doc(
summary="List outgoing relations of organization",
tags=["Organizations", "Organization Relations"],
security=[{"oauth2": ["organization:read"]}],
)
@use_kwargs(OrganizationRelationListRequestSchema, location=("query"))
@marshal_with(OrganizationRelationListResponseSchema)
@ -341,7 +344,6 @@ class OrganizationOutgoingRelationListResource(BaseResource):
@doc(
summary="Add new outgoing relation",
tags=["Organizations", "Organization Relations"],
security=[{"oauth2": ["organization:write"]}],
)
@use_kwargs(OrganizationRelationCreateRequestSchema, location="json", apply=False)
@marshal_with(OrganizationRelationIdSchema, 201)
@ -364,7 +366,6 @@ class OrganizationOrganizationInvitationListResource(BaseResource):
@doc(
summary="List organization invitations of organization",
tags=["Organizations", "Organization Invitations"],
security=[{"oauth2": ["organization:read"]}],
)
@use_kwargs(OrganizationInvitationListRequestSchema, location=("query"))
@marshal_with(OrganizationInvitationListResponseSchema)
@ -380,7 +381,6 @@ class OrganizationOrganizationInvitationListResource(BaseResource):
@doc(
summary="Add new organization invitation",
tags=["Organizations", "Organization Invitations"],
security=[{"oauth2": ["organization:write"]}],
)
@use_kwargs(OrganizationInvitationCreateRequestSchema, location="json", apply=False)
@marshal_with(OrganizationInvitationIdSchema, 201)
@ -413,6 +413,7 @@ class OrganizationEventListListResource(BaseResource):
)
@use_kwargs(EventListListRequestSchema, location=("query"))
@marshal_with(EventListListResponseSchema)
@require_api_access()
def get(self, id, **kwargs):
admin_unit = AdminUnit.query.get_or_404(id)
name = kwargs["name"] if "name" in kwargs else None
@ -423,7 +424,6 @@ class OrganizationEventListListResource(BaseResource):
@doc(
summary="Add new event list",
tags=["Organizations", "Event Lists"],
security=[{"oauth2": ["eventlist:write"]}],
)
@use_kwargs(EventListCreateRequestSchema, location="json", apply=False)
@marshal_with(EventListIdSchema, 201)
@ -449,6 +449,7 @@ class OrganizationEventListStatusListResource(BaseResource):
)
@use_kwargs(EventListListRequestSchema, location=("query"))
@marshal_with(EventListStatusListResponseSchema)
@require_api_access()
def get(self, id, event_id, **kwargs):
admin_unit = AdminUnit.query.get_or_404(id)
name = kwargs["name"] if "name" in kwargs else None
@ -466,6 +467,7 @@ class OrganizationCustomWidgetListResource(BaseResource):
)
@use_kwargs(CustomWidgetListRequestSchema, location=("query"))
@marshal_with(CustomWidgetListResponseSchema)
@require_api_access()
def get(self, id, **kwargs):
admin_unit = AdminUnit.query.get_or_404(id)
name = kwargs["name"] if "name" in kwargs else None
@ -476,7 +478,6 @@ class OrganizationCustomWidgetListResource(BaseResource):
@doc(
summary="Add new custom widget",
tags=["Organizations", "CustomWidgets"],
security=[{"oauth2": ["customwidget:write"]}],
)
@use_kwargs(CustomWidgetPostRequestSchema, location="json", apply=False)
@marshal_with(CustomWidgetIdSchema, 201)

View File

@ -18,7 +18,6 @@ class OrganizationInvitationResource(BaseResource):
@doc(
summary="Get organization invitation",
tags=["Organization Invitations"],
security=[{"oauth2": ["organization:read"]}],
)
@marshal_with(OrganizationInvitationSchema)
@require_api_access("organization:read")
@ -32,7 +31,6 @@ class OrganizationInvitationResource(BaseResource):
@doc(
summary="Update organization invitation",
tags=["Organization Invitations"],
security=[{"oauth2": ["organization:write"]}],
)
@use_kwargs(OrganizationInvitationUpdateRequestSchema, location="json", apply=False)
@marshal_with(None, 204)
@ -52,7 +50,6 @@ class OrganizationInvitationResource(BaseResource):
@doc(
summary="Patch organization invitation",
tags=["Organization Invitations"],
security=[{"oauth2": ["organization:write"]}],
)
@use_kwargs(OrganizationInvitationPatchRequestSchema, location="json", apply=False)
@marshal_with(None, 204)
@ -72,7 +69,6 @@ class OrganizationInvitationResource(BaseResource):
@doc(
summary="Delete organization invitation",
tags=["Organization Invitations"],
security=[{"oauth2": ["organization:write"]}],
)
@marshal_with(None, 204)
@require_api_access("organization:write")

View File

@ -19,7 +19,6 @@ class OrganizationRelationResource(BaseResource):
@doc(
summary="Get organization relation",
tags=["Organization Relations"],
security=[{"oauth2": ["organization:read"]}],
)
@marshal_with(OrganizationRelationSchema)
@require_api_access("organization:read")
@ -37,7 +36,6 @@ class OrganizationRelationResource(BaseResource):
@doc(
summary="Update organization relation",
tags=["Organization Relations"],
security=[{"oauth2": ["organization:write"]}],
)
@use_kwargs(OrganizationRelationUpdateRequestSchema, location="json", apply=False)
@marshal_with(None, 204)
@ -57,7 +55,6 @@ class OrganizationRelationResource(BaseResource):
@doc(
summary="Patch organization relation",
tags=["Organization Relations"],
security=[{"oauth2": ["organization:write"]}],
)
@use_kwargs(OrganizationRelationPatchRequestSchema, location="json", apply=False)
@marshal_with(None, 204)
@ -77,7 +74,6 @@ class OrganizationRelationResource(BaseResource):
@doc(
summary="Delete organization relation",
tags=["Organization Relations"],
security=[{"oauth2": ["organization:write"]}],
)
@marshal_with(None, 204)
@require_api_access("organization:write")

View File

@ -9,25 +9,24 @@ from project.api.organizer.schemas import (
OrganizerPostRequestSchema,
OrganizerSchema,
)
from project.api.resources import BaseResource
from project.api.resources import BaseResource, require_api_access
from project.models import EventOrganizer
from project.oauth2 import require_oauth
class OrganizerResource(BaseResource):
@doc(summary="Get organizer", tags=["Organizers"])
@marshal_with(OrganizerSchema)
@require_api_access()
def get(self, id):
return EventOrganizer.query.get_or_404(id)
@doc(
summary="Update organizer",
tags=["Organizers"],
security=[{"oauth2": ["organizer:write"]}],
)
@use_kwargs(OrganizerPostRequestSchema, location="json", apply=False)
@marshal_with(None, 204)
@require_oauth("organizer:write")
@require_api_access("organizer:write")
def put(self, id):
login_api_user_or_401()
organizer = EventOrganizer.query.get_or_404(id)
@ -41,11 +40,10 @@ class OrganizerResource(BaseResource):
@doc(
summary="Patch organizer",
tags=["Organizers"],
security=[{"oauth2": ["organizer:write"]}],
)
@use_kwargs(OrganizerPatchRequestSchema, location="json", apply=False)
@marshal_with(None, 204)
@require_oauth("organizer:write")
@require_api_access("organizer:write")
def patch(self, id):
login_api_user_or_401()
organizer = EventOrganizer.query.get_or_404(id)
@ -61,10 +59,9 @@ class OrganizerResource(BaseResource):
@doc(
summary="Delete organizer",
tags=["Organizers"],
security=[{"oauth2": ["organizer:write"]}],
)
@marshal_with(None, 204)
@require_oauth("organizer:write")
@require_api_access("organizer:write")
def delete(self, id):
login_api_user_or_401()
organizer = EventOrganizer.query.get_or_404(id)

View File

@ -9,23 +9,24 @@ from project.api.place.schemas import (
PlacePostRequestSchema,
PlaceSchema,
)
from project.api.resources import BaseResource
from project.api.resources import BaseResource, require_api_access
from project.models import EventPlace
from project.oauth2 import require_oauth
class PlaceResource(BaseResource):
@doc(summary="Get place", tags=["Places"])
@marshal_with(PlaceSchema)
@require_api_access()
def get(self, id):
return EventPlace.query.get_or_404(id)
@doc(
summary="Update place", tags=["Places"], security=[{"oauth2": ["place:write"]}]
summary="Update place",
tags=["Places"],
)
@use_kwargs(PlacePostRequestSchema, location="json", apply=False)
@marshal_with(None, 204)
@require_oauth("place:write")
@require_api_access("place:write")
def put(self, id):
login_api_user_or_401()
place = EventPlace.query.get_or_404(id)
@ -36,10 +37,13 @@ class PlaceResource(BaseResource):
return make_response("", 204)
@doc(summary="Patch place", tags=["Places"], security=[{"oauth2": ["place:write"]}])
@doc(
summary="Patch place",
tags=["Places"],
)
@use_kwargs(PlacePatchRequestSchema, location="json", apply=False)
@marshal_with(None, 204)
@require_oauth("place:write")
@require_api_access("place:write")
def patch(self, id):
login_api_user_or_401()
place = EventPlace.query.get_or_404(id)
@ -51,10 +55,11 @@ class PlaceResource(BaseResource):
return make_response("", 204)
@doc(
summary="Delete place", tags=["Places"], security=[{"oauth2": ["place:write"]}]
summary="Delete place",
tags=["Places"],
)
@marshal_with(None, 204)
@require_oauth("place:write")
@require_api_access("place:write")
def delete(self, id):
login_api_user_or_401()
place = EventPlace.query.get_or_404(id)

View File

@ -1,13 +1,13 @@
from functools import wraps
from authlib.oauth2 import OAuth2Error
from authlib.oauth2.rfc6749 import MissingAuthorizationError
from flask import request
from flask_apispec import marshal_with
from flask_apispec.annotations import annotate
from flask_apispec.views import MethodResource
from flask_security import current_user
from flask_wtf.csrf import validate_csrf
from project import db
from project import app, csrf, db
from project.api.schemas import ErrorResponseSchema, UnprocessableEntityResponseSchema
from project.oauth2 import require_oauth
@ -22,23 +22,44 @@ def etag_cache(func):
return wrapper
def require_api_access(scopes=None, optional=False):
def is_internal_request() -> bool:
try:
validate_csrf(csrf._get_csrf_token())
return True
except Exception:
return False
def require_api_access(scopes=None):
def inner_decorator(func):
def wrapped(*args, **kwargs): # see authlib ResourceProtector#__call__
try: # pragma: no cover
try:
require_oauth.acquire_token(scopes)
except MissingAuthorizationError as error:
if optional:
return func(*args, **kwargs)
require_oauth.raise_error_response(error)
except OAuth2Error as error:
require_oauth.raise_error_response(error)
except Exception as e:
if not current_user or not current_user.is_authenticated:
if app.config["API_READ_ANONYM"]:
return func(*args, **kwargs)
if not is_internal_request():
raise e
return func(*args, **kwargs)
scope_list = scopes if type(scopes) is list else [scopes] if scopes else list()
security = [{"oauth2AuthCode": scope_list}]
if not scope_list:
security.append(
{
"oauth2ClientCredentials": scope_list,
}
)
annotate(
wrapped,
"docs",
[{"security": security}],
)
return wrapped
return inner_decorator

View File

@ -34,7 +34,6 @@ class UserOrganizationInvitationListResource(BaseResource):
@doc(
summary="List organization invitations of user",
tags=["Users", "Organization Invitations"],
security=[{"oauth2": ["user:read"]}],
)
@use_kwargs(OrganizationInvitationListRequestSchema, location=("query"))
@marshal_with(OrganizationInvitationListResponseSchema)
@ -53,7 +52,6 @@ class UserOrganizationInvitationResource(BaseResource):
@doc(
summary="Get organization invitation of user",
tags=["Users", "Organization Invitations"],
security=[{"oauth2": ["user:read"]}],
)
@marshal_with(OrganizationInvitationSchema)
@require_api_access("user:read")
@ -67,7 +65,6 @@ class UserOrganizationInvitationResource(BaseResource):
@doc(
summary="Delete organization invitation of user",
tags=["Users", "Organization Invitations"],
security=[{"oauth2": ["user:write"]}],
)
@marshal_with(None, 204)
@require_api_access("user:write")
@ -86,7 +83,6 @@ class UserFavoriteEventListResource(BaseResource):
@doc(
summary="List favorite events of user",
tags=["Users", "Events"],
security=[{"oauth2": ["user:read"]}],
)
@use_kwargs(UserFavoriteEventListRequestSchema, location=("query"))
@marshal_with(UserFavoriteEventListResponseSchema)
@ -104,7 +100,6 @@ class UserFavoriteEventSearchResource(BaseResource):
@doc(
summary="Search for favorite events of user",
tags=["Users", "Events"],
security=[{"oauth2": ["user:read"]}],
)
@use_kwargs(EventSearchRequestSchema, location=("query"))
@marshal_with(EventSearchResponseSchema)
@ -124,7 +119,6 @@ class UserFavoriteEventListWriteResource(BaseResource):
@doc(
summary="Add event to users favorites",
tags=["Users", "Events"],
security=[{"oauth2": ["user:write"]}],
)
@marshal_with(None, 204)
@require_api_access("user:write")
@ -142,7 +136,6 @@ class UserFavoriteEventListWriteResource(BaseResource):
@doc(
summary="Remove event from users favorites",
tags=["Users", "Events"],
security=[{"oauth2": ["user:write"]}],
)
@marshal_with(None, 204)
@require_api_access("user:write")

View File

@ -258,9 +258,6 @@ def _insert_default_oauth2_client(user_id):
metadata = dict()
metadata["client_name"] = "Mein Client"
metadata["scope"] = " ".join(scope_list)
metadata["grant_types"] = ["authorization_code", "refresh_token"]
metadata["response_types"] = ["code"]
metadata["token_endpoint_auth_method"] = "client_secret_post"
metadata["redirect_uris"] = ["/"]
client.set_client_metadata(metadata)

View File

@ -3,7 +3,7 @@ import os
from flask_babel import lazy_gettext
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, TextAreaField
from wtforms.validators import DataRequired
from wtforms.validators import DataRequired, Optional
from project.api import scopes
from project.forms.widgets import MultiCheckboxField
@ -13,11 +13,11 @@ from project.utils import split_by_crlf
class BaseOAuth2ClientForm(FlaskForm):
client_name = StringField(lazy_gettext("Client name"), validators=[DataRequired()])
redirect_uris = TextAreaField(
lazy_gettext("Redirect URIs"), validators=[DataRequired()]
lazy_gettext("Redirect URIs"), validators=[Optional()]
)
scope = MultiCheckboxField(
lazy_gettext("Scopes"),
validators=[DataRequired()],
validators=[Optional()],
choices=[(k, k) for k, v in scopes.items()],
)

View File

@ -47,16 +47,7 @@ def create_initial_data():
"reference_request:delete",
"reference_request:verify",
]
early_adopter_permissions = [
"oauth2_client:create",
"oauth2_client:read",
"oauth2_client:update",
"oauth2_client:delete",
"oauth2_token:create",
"oauth2_token:read",
"oauth2_token:update",
"oauth2_token:delete",
]
early_adopter_permissions = []
upsert_admin_unit_member_role("admin", "Administrator", admin_permissions)
upsert_admin_unit_member_role("event_verifier", "Event expert", event_permissions)

View File

@ -276,7 +276,7 @@ class AdminUnit(db.Model, TrackableMixin):
server_default="0",
)
)
incoming_verification_requests_text = Column(UnicodeText())
incoming_verification_requests_text = deferred(Column(UnicodeText()))
can_invite_other = deferred(
Column(
Boolean(),

View File

@ -22,7 +22,7 @@ class OAuth2Client(db.Model, OAuth2ClientMixin):
@OAuth2ClientMixin.grant_types.getter
def grant_types(self):
return ["authorization_code", "refresh_token"]
return ["authorization_code", "refresh_token", "client_credentials"]
@OAuth2ClientMixin.response_types.getter
def response_types(self):

View File

@ -71,6 +71,10 @@ class AuthorizationCodeGrant(grants.AuthorizationCodeGrant):
return db.session.get(User, authorization_code.user_id)
class ClientCredentialsGrant(grants.ClientCredentialsGrant):
TOKEN_ENDPOINT_AUTH_METHODS = ["client_secret_basic", "client_secret_post"]
class OpenIDCode(_OpenIDCode):
def exists_nonce(self, nonce, request):
return exists_nonce(nonce, request)
@ -176,6 +180,7 @@ def config_oauth(app):
AuthorizationCodeGrant,
[CodeChallenge(required=True), OpenIDCode()],
)
authorization.register_grant(ClientCredentialsGrant)
authorization.register_grant(RefreshTokenGrant)
# support revocation

View File

@ -44,6 +44,8 @@ def set_manage_admin_unit_cookie(response):
@app.after_request
def set_response_headers(response):
if request and request.endpoint:
if request.endpoint.startswith("api_"):
return response
if request.endpoint != "static" and request.endpoint != "widget_event_dates":
response.headers["X-Frame-Options"] = "SAMEORIGIN"

View File

@ -411,7 +411,7 @@ const WidgetConfigurator = {
}),
computed: {
iFrameSource() {
return `${window.location.origin}/static/widget/${this.widgetType}.html`;
return `${window.location.origin}/custom_widget/${this.widgetType}`;
},
organizationId() {
return this.$route.params.organization_id;

View File

@ -38,11 +38,11 @@
var googleTagManager = false;
if (customId != null) {
var url = baseUrl + "/api/v1/custom-widgets/" + customId;
var url = baseUrl + "/js/wlcw/" + customId;
customWidgetData = loadJSON(url);
var settings = customWidgetData.settings;
src = baseUrl + "/static/widget/" + customWidgetData.widget_type + ".html";
src = baseUrl + "/custom_widget/" + customWidgetData.widget_type;
if (settings.hasOwnProperty('iFrameAutoResize') && settings.iFrameAutoResize != null) {
resize = settings.iFrameAutoResize;
@ -233,6 +233,8 @@
xmlhttp.overrideMimeType(mimeType);
}
xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token() }}");
xmlhttp.send();
return xmlhttp.responseText;
}

View File

@ -1687,24 +1687,6 @@ $('#allday').on('change', function() {
</script>
{% endmacro %}
{% macro render_ajax_csrf_script() %}
<script type="text/javascript">
{{ render_ajax_csrf() }}
</script>
{% endmacro %}
{% macro render_ajax_csrf() %}
var csrf_token = "{{ csrf_token() }}";
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrf_token);
}
}
});
{% endmacro %}
{% macro render_form_scripts() %}
<script src="{{ url_for('static', filename='ext/jquery-ui.1.12.1/jquery-ui.min.js')}}"></script>
<script src="{{ url_for('static', filename='ext/jquery-ui-i18n.1.11.4.min.js')}}"></script>
@ -1712,8 +1694,6 @@ $('#allday').on('change', function() {
<script src="{{ url_for('static', filename='ext/select2.i18n.de.4.1.0-beta.1.min.js')}}"></script>
<script src="{{ url_for('static', filename='ext/jquery.timepicker.1.13.18.min.js')}}"></script>
<script type="text/javascript">
{{ render_ajax_csrf() }}
$.datepicker.setDefaults($.datepicker.regional["de"]);
$.fn.select2.defaults.set("language", "de");

View File

@ -96,6 +96,7 @@
<script src="/static/ext/moment.2.24.0.with-locales.min.js"></script>
<script>
axios.defaults.baseURL = window.location.origin;
axios.defaults.headers.common["X-CSRFToken"] = "[% csrf_token() %]";
moment.locale("de");
Vue.component('event-warning-pills', {

View File

@ -234,6 +234,7 @@
</div>
<script>
axios.defaults.baseURL = window.location.origin;
axios.defaults.headers.common["X-CSRFToken"] = "[% csrf_token() %]";
moment.locale("de");
const localizedMessages = {

View File

@ -138,6 +138,16 @@
{% endfor %}
{% endif %}
<script type="text/javascript">
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token() }}");
}
}
});
</script>
{% block header %}
{% endblock %}
{%- endblock head %}

View File

@ -331,6 +331,7 @@
});
axios.defaults.baseURL = "{{ get_base_url() }}";
axios.defaults.headers.common["X-CSRFToken"] = "{{ csrf_token() }}";
axios.interceptors.request.use(
function (config) {
if (config) {

View File

@ -1,10 +1,8 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_ajax_csrf_script %}
{%- block title -%}
{{ _('Export') }}
{%- endblock -%}
{% block header %}
{{ render_ajax_csrf_script() }}
<script>
function submit_async() {
$("#submit_async"). prop("disabled", true);

View File

@ -12,11 +12,9 @@
</ol>
</nav>
{% if current_user.has_permission('oauth2_client:create') %}
<div class="my-4">
<a class="btn btn-outline-secondary my-1" href="{{ url_for('oauth2_client_create') }}" role="button"><i class="fa fa-plus"></i> {{ _('Create OAuth2 client') }}</a>
</div>
{% endif %}
<div class="table-responsive">
<table class="table table-sm table-bordered table-hover table-striped">

View File

@ -42,7 +42,6 @@
</a>
</div>
{% if current_user.has_permission('oauth2_client:read') %}
<h2>{{ _('Developer') }}</h2>
<div class="list-group">
@ -51,6 +50,5 @@
<i class="fa fa-caret-right"></i>
</a>
</div>
{% endif %}
{% endblock %}

View File

@ -175,7 +175,7 @@ def admin_unit_update(id):
@auth_required()
@manage_required("admin_unit:update")
def admin_unit_request_deletion(id):
admin_unit = g.admin_unit
admin_unit = g.manage_admin_unit
if admin_unit.deletion_requested_at: # pragma: no cover
return redirect(url_for("admin_unit_cancel_deletion", id=admin_unit.id))
@ -208,7 +208,7 @@ def admin_unit_request_deletion(id):
@auth_required()
@manage_required("admin_unit:update")
def admin_unit_cancel_deletion(id):
admin_unit = g.admin_unit
admin_unit = g.manage_admin_unit
if not admin_unit.deletion_requested_at: # pragma: no cover
return redirect(url_for("admin_unit_request_deletion", id=admin_unit.id))

View File

@ -0,0 +1,8 @@
from project import app
@app.route("/custom_widget/<string:type>")
def custom_widget_type(type: str):
env = app.jinja_env.overlay(variable_start_string="[%", variable_end_string="%]")
template = env.get_template(f"custom_widget/{type}.html")
return template.render()

View File

@ -1,12 +1,14 @@
from flask import request
from flask.json import jsonify
from flask_babel import gettext
from flask_cors import cross_origin
from flask_security import url_for_security
from flask_security.utils import localize_callback
from project import app, csrf
from project.api.custom_widget.schemas import CustomWidgetSchema
from project.maputils import find_gmaps_places, get_gmaps_place
from project.models import AdminUnit
from project.models import AdminUnit, CustomWidget
from project.services.place import get_event_places
from project.services.user import find_user_by_email
from project.utils import get_place_str
@ -124,3 +126,11 @@ def js_autocomplete_gmaps_place():
gmaps_id = request.args["gmaps_id"]
place = get_gmaps_place(gmaps_id)
return jsonify(place)
@app.route("/js/wlcw/<int:id>")
@cross_origin()
def js_widget_loader_custom_widget(id: int):
widget = CustomWidget.query.get_or_404(id)
schema = CustomWidgetSchema()
return schema.dump(widget)

View File

@ -1,6 +1,6 @@
from flask import flash, redirect, render_template, url_for
from flask_babel import gettext
from flask_security import current_user, permissions_required
from flask_security import current_user
from sqlalchemy.exc import SQLAlchemyError
from project import app, db
@ -21,7 +21,6 @@ from project.views.utils import (
@app.route("/oauth2_client/create", methods=("GET", "POST"))
@permissions_required("oauth2_client:create")
def oauth2_client_create():
form = CreateOAuth2ClientForm()
@ -46,7 +45,6 @@ def oauth2_client_create():
@app.route("/oauth2_client/<int:id>/update", methods=("GET", "POST"))
@permissions_required("oauth2_client:update")
def oauth2_client_update(id):
oauth2_client = OAuth2Client.query.get_or_404(id)
owner_access_or_401(oauth2_client.user_id)
@ -73,7 +71,6 @@ def oauth2_client_update(id):
@app.route("/oauth2_client/<int:id>/delete", methods=("GET", "POST"))
@permissions_required("oauth2_client:delete")
def oauth2_client_delete(id):
oauth2_client = OAuth2Client.query.get_or_404(id)
owner_access_or_401(oauth2_client.user_id)
@ -101,7 +98,6 @@ def oauth2_client_delete(id):
@app.route("/oauth2_client/<int:id>")
@permissions_required("oauth2_client:read")
def oauth2_client(id):
oauth2_client = OAuth2Client.query.get_or_404(id)
owner_access_or_401(oauth2_client.user_id)
@ -113,7 +109,6 @@ def oauth2_client(id):
@app.route("/oauth2_clients")
@permissions_required("oauth2_client:read")
def oauth2_clients():
oauth2_clients = (
OAuth2Client.query.filter(OAuth2Client.user_id == current_user.id)

View File

@ -317,8 +317,7 @@ def manage_required(permission=None):
url_for("manage_admin_unit", id=admin_unit.id)
)
g.admin_unit = admin_unit
set_current_admin_unit(admin_unit)
return f(id, *args, **kwargs)
return decorated_function

View File

@ -1,5 +1,9 @@
def test_read(client, seeder, utils):
_, admin_unit_id = seeder.setup_base()
from tests.seeder import Seeder
from tests.utils import UtilActions
def test_read(client, seeder: Seeder, utils: UtilActions):
_, admin_unit_id = seeder.setup_api_access(user_access=False)
custom_widget_id = seeder.insert_event_custom_widget(admin_unit_id)
url = utils.get_url("api_v1_custom_widget", id=custom_widget_id)
@ -8,7 +12,7 @@ def test_read(client, seeder, utils):
assert response.json["settings"]["color"] == "black"
def test_put(client, seeder, utils, app, db):
def test_put(client, seeder: Seeder, utils: UtilActions, app, db):
_, admin_unit_id = seeder.setup_api_access()
custom_widget_id = seeder.insert_event_custom_widget(admin_unit_id)
@ -24,7 +28,7 @@ def test_put(client, seeder, utils, app, db):
assert custom_widget.widget_type == "search"
def test_patch(client, seeder, utils, app, db):
def test_patch(client, seeder: Seeder, utils: UtilActions, app, db):
_, admin_unit_id = seeder.setup_api_access()
custom_widget_id = seeder.insert_event_custom_widget(admin_unit_id)
@ -40,7 +44,7 @@ def test_patch(client, seeder, utils, app, db):
assert custom_widget.widget_type == "search"
def test_delete(client, seeder, utils, app, db):
def test_delete(client, seeder: Seeder, utils: UtilActions, app, db):
_, admin_unit_id = seeder.setup_api_access()
custom_widget_id = seeder.insert_event_custom_widget(admin_unit_id)

View File

@ -1,3 +1,9 @@
def test_read(client, seeder, utils):
response = utils.get_endpoint("api_v1_dump")
from tests.seeder import Seeder
from tests.utils import UtilActions
def test_read(client, seeder: Seeder, utils: UtilActions):
_, admin_unit_id = seeder.setup_api_access()
url = utils.get_url("api_v1_dump")
response = utils.get_json(url)
utils.assert_response_notFound(response)

View File

@ -3,10 +3,11 @@ import base64
import pytest
from tests.seeder import Seeder
from tests.utils import UtilActions
def test_read(client, app, db, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
def test_read(client, app, db, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
event_id = seeder.create_event(admin_unit_id)
with app.app_context():
@ -20,12 +21,21 @@ def test_read(client, app, db, seeder, utils):
db.session.commit()
url = utils.get_url("api_v1_event", id=event_id)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert response.json["status"] == "scheduled"
def test_read_otherDraft(client, app, db, seeder, utils):
def test_read_anonym(client, app, db, seeder: Seeder, utils: UtilActions):
app.config["API_READ_ANONYM"] = True
user_id, admin_unit_id = seeder.setup_base(log_in=False)
event_id = seeder.create_event(admin_unit_id)
url = utils.get_url("api_v1_event", id=event_id)
utils.get_json_ok(url)
def test_read_otherDraft(client, app, db, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
event_id = seeder.create_event(admin_unit_id, draft=True)
url = utils.get_url("api_v1_event", id=event_id)
@ -33,7 +43,7 @@ def test_read_otherDraft(client, app, db, seeder, utils):
utils.assert_response_unauthorized(response)
def test_read_myDraft(client, app, db, seeder, utils):
def test_read_myDraft(client, app, db, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id, draft=True)
@ -43,7 +53,7 @@ def test_read_myDraft(client, app, db, seeder, utils):
assert response.json["public_status"] == "draft"
def test_read_otherUnverified(client, app, db, seeder, utils):
def test_read_otherUnverified(client, app, db, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_base(log_in=False, admin_unit_verified=False)
event_id = seeder.create_event(admin_unit_id, draft=True)
@ -52,7 +62,7 @@ def test_read_otherUnverified(client, app, db, seeder, utils):
utils.assert_response_unauthorized(response)
def test_read_myUnverified(client, app, db, seeder, utils):
def test_read_myUnverified(client, app, db, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(admin_unit_verified=False)
event_id = seeder.create_event(admin_unit_id)
@ -61,8 +71,8 @@ def test_read_myUnverified(client, app, db, seeder, utils):
utils.assert_response_ok(response)
def test_read_co_organizers(client, app, db, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
def test_read_co_organizers(client, app, db, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
event_id, organizer_a_id, organizer_b_id = seeder.create_event_with_co_organizers(
admin_unit_id
)
@ -74,20 +84,20 @@ def test_read_co_organizers(client, app, db, seeder, utils):
assert response.json["co_organizers"][1]["id"] == organizer_b_id
def test_list(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
def test_list(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
event_id = seeder.create_event(admin_unit_id)
seeder.create_event(admin_unit_id, draft=True)
seeder.create_event_unverified()
url = utils.get_url("api_v1_event_list")
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["id"] == event_id
def test_search(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
def test_search(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
event_id = seeder.create_event(admin_unit_id)
image_id = seeder.upsert_default_image()
seeder.assign_image_to_event(event_id, image_id)
@ -95,49 +105,52 @@ def test_search(client, seeder, utils):
seeder.create_event_unverified()
url = utils.get_url("api_v1_event_search")
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["id"] == event_id
def test_search_is_favored(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
def test_search_is_favored(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
seeder.add_favorite_event(user_id, event_id)
url = utils.get_url("api_v1_event_search")
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["id"] == event_id
assert response.json["items"][0]["is_favored"]
def test_dates(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base(log_in=False)
def test_dates(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
event_id = seeder.create_event(admin_unit_id)
url = utils.get_url("api_v1_event_dates", id=event_id)
utils.get_ok(url)
response = utils.get_json(url)
utils.assert_response_ok(response)
event_id = seeder.create_event(admin_unit_id, draft=True)
url = utils.get_url("api_v1_event_dates", id=event_id)
response = utils.get(url)
response = utils.get_json(url)
utils.assert_response_unauthorized(response)
_, _, event_id = seeder.create_event_unverified()
url = utils.get_url("api_v1_event_dates", id=event_id)
response = utils.get(url)
response = utils.get_json(url)
utils.assert_response_unauthorized(response)
event_id = seeder.create_event(
admin_unit_id, recurrence_rule="RRULE:FREQ=DAILY;COUNT=51"
)
url = utils.get_url("api_v1_event_dates", id=event_id)
utils.get_ok(url)
response = utils.get_json(url)
utils.assert_response_ok(response)
url = utils.get_url("api_v1_event_dates", id=event_id, page=2)
utils.get_ok(url)
response = utils.get_json(url)
utils.assert_response_ok(response)
def test_dates_myDraft(client, seeder, utils):
def test_dates_myDraft(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id, draft=True)
@ -146,7 +159,7 @@ def test_dates_myDraft(client, seeder, utils):
utils.assert_response_ok(response)
def test_dates_myUnverified(client, seeder, utils):
def test_dates_myUnverified(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(admin_unit_verified=False)
event_id = seeder.create_event(admin_unit_id)
@ -180,7 +193,7 @@ def create_put(
@pytest.mark.parametrize(
"variant", ["normal", "legacy", "recurrence", "two_date_definitions"]
)
def test_put(client, seeder, utils, app, db, mocker, variant):
def test_put(client, seeder: Seeder, utils: UtilActions, app, db, mocker, variant):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -270,7 +283,7 @@ def test_put(client, seeder, utils, app, db, mocker, variant):
assert len_dates == 1
def test_put_invalidRecurrenceRule(client, seeder, utils, app):
def test_put_invalidRecurrenceRule(client, seeder: Seeder, utils: UtilActions, app):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -284,7 +297,7 @@ def test_put_invalidRecurrenceRule(client, seeder, utils, app):
utils.assert_response_unprocessable_entity(response)
def test_put_missingName(client, seeder, utils, app):
def test_put_missingName(client, seeder: Seeder, utils: UtilActions, app):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -298,7 +311,7 @@ def test_put_missingName(client, seeder, utils, app):
utils.assert_response_unprocessable_entity(response)
def test_put_missingPlace(client, seeder, utils, app):
def test_put_missingPlace(client, seeder: Seeder, utils: UtilActions, app):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -312,7 +325,7 @@ def test_put_missingPlace(client, seeder, utils, app):
utils.assert_response_unprocessable_entity(response)
def test_put_placeFromAnotherAdminUnit(client, seeder, utils, app):
def test_put_placeFromAnotherAdminUnit(client, seeder: Seeder, utils: UtilActions, app):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
organizer_id = seeder.upsert_default_event_organizer(admin_unit_id)
@ -326,7 +339,7 @@ def test_put_placeFromAnotherAdminUnit(client, seeder, utils, app):
utils.assert_response_api_error(response, "Check Violation")
def test_put_missingOrganizer(client, seeder, utils, app):
def test_put_missingOrganizer(client, seeder: Seeder, utils: UtilActions, app):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -340,7 +353,9 @@ def test_put_missingOrganizer(client, seeder, utils, app):
utils.assert_response_unprocessable_entity(response)
def test_put_organizerFromAnotherAdminUnit(client, seeder, utils, app):
def test_put_organizerFromAnotherAdminUnit(
client, seeder: Seeder, utils: UtilActions, app
):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -354,7 +369,7 @@ def test_put_organizerFromAnotherAdminUnit(client, seeder, utils, app):
utils.assert_response_api_error(response, "Check Violation")
def test_put_co_organizers(client, seeder, utils, app, db):
def test_put_co_organizers(client, seeder: Seeder, utils: UtilActions, app, db):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -381,7 +396,9 @@ def test_put_co_organizers(client, seeder, utils, app, db):
assert event.co_organizers[1].id == organizer_b_id
def test_put_co_organizerFromAnotherAdminUnit(client, seeder, utils, app):
def test_put_co_organizerFromAnotherAdminUnit(
client, seeder: Seeder, utils: UtilActions, app
):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -401,7 +418,7 @@ def test_put_co_organizerFromAnotherAdminUnit(client, seeder, utils, app):
utils.assert_response_api_error(response, "Check Violation")
def test_put_invalidDateFormat(client, seeder, utils, app):
def test_put_invalidDateFormat(client, seeder: Seeder, utils: UtilActions, app):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -414,7 +431,7 @@ def test_put_invalidDateFormat(client, seeder, utils, app):
utils.assert_response_unprocessable_entity(response)
def test_put_startAfterEnd(client, seeder: Seeder, utils, app):
def test_put_startAfterEnd(client, seeder: Seeder, utils: UtilActions, app):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -429,7 +446,9 @@ def test_put_startAfterEnd(client, seeder: Seeder, utils, app):
utils.assert_response_bad_request(response)
def test_put_durationMoreThanMaxAllowedDuration(client, seeder, utils, app):
def test_put_durationMoreThanMaxAllowedDuration(
client, seeder: Seeder, utils: UtilActions, app
):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -444,7 +463,7 @@ def test_put_durationMoreThanMaxAllowedDuration(client, seeder, utils, app):
utils.assert_response_bad_request(response)
def test_put_categories(client, seeder, utils, app, db):
def test_put_categories(client, seeder: Seeder, utils: UtilActions, app, db):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -465,7 +484,7 @@ def test_put_categories(client, seeder, utils, app, db):
assert event.category.name == "Art"
def test_put_dateWithTimezone(client, seeder, utils, app, db):
def test_put_dateWithTimezone(client, seeder: Seeder, utils: UtilActions, app, db):
from project.dateutils import create_berlin_date
user_id, admin_unit_id = seeder.setup_api_access()
@ -488,7 +507,7 @@ def test_put_dateWithTimezone(client, seeder, utils, app, db):
assert event.date_definitions[0].start == expected
def test_put_dateWithoutTimezone(client, seeder, utils, app, db):
def test_put_dateWithoutTimezone(client, seeder: Seeder, utils: UtilActions, app, db):
from project.dateutils import create_berlin_date
user_id, admin_unit_id = seeder.setup_api_access()
@ -511,7 +530,9 @@ def test_put_dateWithoutTimezone(client, seeder, utils, app, db):
assert event.date_definitions[0].start == expected
def test_put_referencedEventUpdate_sendsMail(client, seeder, utils, app, mocker):
def test_put_referencedEventUpdate_sendsMail(
client, seeder: Seeder, utils: UtilActions, app, mocker
):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event_via_api(admin_unit_id)
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -532,7 +553,7 @@ def test_put_referencedEventUpdate_sendsMail(client, seeder, utils, app, mocker)
def test_put_referencedEventNonDirtyUpdate_doesNotSendMail(
client, seeder, utils, app, mocker
client, seeder: Seeder, utils: UtilActions, app, mocker
):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event_via_api(admin_unit_id)
@ -553,7 +574,7 @@ def test_put_referencedEventNonDirtyUpdate_doesNotSendMail(
mail_mock.assert_not_called()
def test_patch(client, seeder, utils, app, db):
def test_patch(client, seeder: Seeder, utils: UtilActions, app, db):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
@ -569,7 +590,7 @@ def test_patch(client, seeder, utils, app, db):
assert event.description == "Neu"
def test_patch_startAfterEnd(client, seeder, utils, app, db):
def test_patch_startAfterEnd(client, seeder: Seeder, utils: UtilActions, app, db):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
@ -586,7 +607,9 @@ def test_patch_startAfterEnd(client, seeder, utils, app, db):
utils.assert_response_bad_request(response)
def test_patch_referencedEventUpdate_sendsMail(client, seeder, utils, app, mocker):
def test_patch_referencedEventUpdate_sendsMail(
client, seeder: Seeder, utils: UtilActions, app, mocker
):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event_via_api(admin_unit_id)
@ -602,7 +625,9 @@ def test_patch_referencedEventUpdate_sendsMail(client, seeder, utils, app, mocke
utils.assert_send_mail_called(mail_mock, "other@test.de")
def test_patch_photo(client, seeder, utils, app, db, requests_mock):
def test_patch_photo(
client, seeder: Seeder, utils: UtilActions, app, db, requests_mock
):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
@ -625,7 +650,7 @@ def test_patch_photo(client, seeder, utils, app, db, requests_mock):
assert event.photo.encoding_format == "image/png"
def test_patch_photo_copyright(client, db, seeder, utils, app):
def test_patch_photo_copyright(client, db, seeder: Seeder, utils: UtilActions, app):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
image_id = seeder.upsert_default_image()
@ -647,7 +672,7 @@ def test_patch_photo_copyright(client, db, seeder, utils, app):
assert event.photo.copyright_text == "Heiner"
def test_patch_photo_delete(client, db, seeder, utils, app):
def test_patch_photo_delete(client, db, seeder: Seeder, utils: UtilActions, app):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
image_id = seeder.upsert_default_image()
@ -670,7 +695,7 @@ def test_patch_photo_delete(client, db, seeder, utils, app):
assert image is None
def test_delete(client, seeder, utils, app, db):
def test_delete(client, seeder: Seeder, utils: UtilActions, app, db):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
@ -685,8 +710,8 @@ def test_delete(client, seeder, utils, app, db):
assert event is None
def test_report_mail(client, seeder, utils, app, mocker):
user_id, admin_unit_id = seeder.setup_base(admin=False, log_in=False)
def test_report_mail(client, seeder: Seeder, utils: UtilActions, app, mocker):
user_id, admin_unit_id = seeder.setup_api_access(admin=False, user_access=False)
event_id = seeder.create_event(admin_unit_id)
seeder.create_user(email="admin@test.de", admin=True)
seeder.create_user(email="normal@test.de", admin=False)

View File

@ -1,6 +1,10 @@
def test_list(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
from tests.seeder import Seeder
from tests.utils import UtilActions
def test_list(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
seeder.create_event(admin_unit_id)
url = utils.get_url("api_v1_event_category_list")
utils.get_ok(url)
utils.get_json_ok(url)

View File

@ -1,17 +1,22 @@
def test_read(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base(log_in=False)
from tests.seeder import Seeder
from tests.utils import UtilActions
def test_read(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
seeder.create_event(admin_unit_id)
url = utils.get_url("api_v1_event_date", id=1)
utils.get_ok(url)
response = utils.get_json(url)
utils.assert_response_ok(response)
seeder.create_event(admin_unit_id, draft=True)
draft_url = utils.get_url("api_v1_event_date", id=2)
response = utils.get(draft_url)
response = utils.get_json(draft_url)
utils.assert_response_unauthorized(response)
seeder.create_event_unverified()
unverified_url = utils.get_url("api_v1_event_date", id=3)
response = utils.get(unverified_url)
response = utils.get_json(unverified_url)
utils.assert_response_unauthorized(response)
seeder.authorize_api_access(user_id, admin_unit_id)
@ -19,7 +24,7 @@ def test_read(client, seeder, utils):
utils.assert_response_ok(response)
def test_read_myUnverified(client, seeder, utils):
def test_read_myUnverified(client, seeder: Seeder, utils: UtilActions):
_, admin_unit_id = seeder.setup_api_access(admin_unit_verified=False)
seeder.create_event(admin_unit_id)
@ -28,75 +33,75 @@ def test_read_myUnverified(client, seeder, utils):
utils.assert_response_ok(response)
def test_list(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base(log_in=False)
def test_list(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
seeder.create_event(admin_unit_id)
seeder.create_event(admin_unit_id, draft=True)
seeder.create_event_unverified()
url = utils.get_url("api_v1_event_date_list")
response = utils.get_ok(url)
response = utils.get_json(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["id"] == 1
def test_search(client, seeder, utils, app, db):
def test_search(client, seeder: Seeder, utils: UtilActions, app, db):
from project.dateutils import create_berlin_date
user_id, admin_unit_id = seeder.setup_base()
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
start = create_berlin_date(2020, 10, 3, 10)
event_id = seeder.create_event(admin_unit_id, start=start)
seeder.create_event(admin_unit_id, draft=True)
seeder.create_event_unverified()
url = utils.get_url("api_v1_event_date_search", sort="-rating")
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["event"]["id"] == event_id
assert response.json["items"][0]["start"] == "2020-10-03T10:00:00+02:00"
url = utils.get_url("api_v1_event_date_search", keyword="name")
response = utils.get_ok(url)
response = utils.get_json_ok(url)
url = utils.get_url("api_v1_event_date_search", category_id=0)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
url = utils.get_url("api_v1_event_date_search", category_id=2000)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
url = utils.get_url("api_v1_event_date_search", weekday=1)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
url = utils.get_url(
"api_v1_event_date_search", date_from="2020-10-03", date_to="2021-10-03"
)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
url = utils.get_url(
"api_v1_event_date_search", coordinate="51.9077888,10.4333312", distance=500
)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
organizer_id = seeder.upsert_default_event_organizer(admin_unit_id)
url = utils.get_url("api_v1_event_date_search", organizer_id=organizer_id)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
event_place_id = seeder.upsert_default_event_place(admin_unit_id)
url = utils.get_url("api_v1_event_date_search", event_place_id=event_place_id)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
url = utils.get_url("api_v1_event_date_search", organization_id=admin_unit_id)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
listed_event_id = seeder.create_event(admin_unit_id)
event_list_id = seeder.create_event_list(admin_unit_id, listed_event_id)
url = utils.get_url("api_v1_event_date_search", event_list_id=event_list_id)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["event"]["id"] == listed_event_id
url = utils.get_url("api_v1_event_date_search", status="scheduled")
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 2
with app.app_context():
@ -107,18 +112,18 @@ def test_search(client, seeder, utils, app, db):
db.session.commit()
url = utils.get_url("api_v1_event_date_search", status="scheduled")
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
url = utils.get_url("api_v1_event_date_search", status=["scheduled", "cancelled"])
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 2
def test_search_oneDay(client, seeder, utils):
def test_search_oneDay(client, seeder: Seeder, utils: UtilActions):
from project.dateutils import create_berlin_date
user_id, admin_unit_id = seeder.setup_base()
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
start = create_berlin_date(2020, 10, 3, 10)
end = create_berlin_date(2020, 10, 3, 11)
@ -128,18 +133,18 @@ def test_search_oneDay(client, seeder, utils):
url = utils.get_url(
"api_v1_event_date_search", date_from="2020-10-03", date_to="2020-10-03"
)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["event"]["id"] == event_id
def test_search_is_favored(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
def test_search_is_favored(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
seeder.add_favorite_event(user_id, event_id)
url = utils.get_url("api_v1_event_date_search")
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["event"]["id"] == event_id
assert response.json["items"][0]["event"]["is_favored"]

View File

@ -1,13 +1,17 @@
def test_read(client, app, db, seeder, utils):
_, admin_unit_id = seeder.setup_base()
from tests.seeder import Seeder
from tests.utils import UtilActions
def test_read(client, app, db, seeder: Seeder, utils: UtilActions):
_, admin_unit_id = seeder.setup_api_access(user_access=False)
event_list_id = seeder.create_event_list(admin_unit_id)
url = utils.get_url("api_v1_event_list_model", id=event_list_id)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert response.json["id"] == event_list_id
def test_put(client, seeder, utils, app, db):
def test_put(client, seeder: Seeder, utils: UtilActions, app, db):
_, admin_unit_id = seeder.setup_api_access()
event_list_id = seeder.create_event_list(admin_unit_id)
@ -22,7 +26,7 @@ def test_put(client, seeder, utils, app, db):
assert event_list.name == "Neuer Name"
def test_patch(client, seeder, utils, app, db):
def test_patch(client, seeder: Seeder, utils: UtilActions, app, db):
_, admin_unit_id = seeder.setup_api_access()
event_list_id = seeder.create_event_list(admin_unit_id)
@ -37,7 +41,7 @@ def test_patch(client, seeder, utils, app, db):
assert event_list.name == "Neuer Name"
def test_delete(client, seeder, utils, app, db):
def test_delete(client, seeder: Seeder, utils: UtilActions, app, db):
_, admin_unit_id = seeder.setup_api_access()
event_list_id = seeder.create_event_list(admin_unit_id)
@ -52,18 +56,18 @@ def test_delete(client, seeder, utils, app, db):
assert event_list is None
def test_events(client, seeder, utils):
_, admin_unit_id = seeder.setup_base()
def test_events(client, seeder: Seeder, utils: UtilActions):
_, admin_unit_id = seeder.setup_api_access(user_access=False)
event_id = seeder.create_event(admin_unit_id)
event_list_id = seeder.create_event_list(admin_unit_id, event_id)
url = utils.get_url("api_v1_event_list_event_list", id=event_list_id)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["id"] == event_id
def test_events_put(client, seeder, utils, app, db):
def test_events_put(client, seeder: Seeder, utils: UtilActions, app, db):
_, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
event_list_id = seeder.create_event_list(admin_unit_id)
@ -82,7 +86,7 @@ def test_events_put(client, seeder, utils, app, db):
assert event_list.events[0].id == event_id
def test_events_delete(client, seeder, utils, app, db):
def test_events_delete(client, seeder: Seeder, utils: UtilActions, app, db):
_, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
event_list_id = seeder.create_event_list(admin_unit_id, event_id)

View File

@ -1,5 +1,9 @@
def test_read(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
from tests.seeder import Seeder
from tests.utils import UtilActions
def test_read(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
(
other_user_id,
other_admin_unit_id,
@ -8,4 +12,4 @@ def test_read(client, seeder, utils):
) = seeder.create_any_reference(admin_unit_id)
url = utils.get_url("api_v1_event_reference", id=reference_id)
utils.get_ok(url)
utils.get_json_ok(url)

View File

@ -1,27 +1,30 @@
import pytest
from tests.seeder import Seeder
from tests.utils import UtilActions
def test_read(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base(admin=True, log_in=False)
def test_read(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
url = utils.get_url("api_v1_organization", id=admin_unit_id)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert "can_verify_other" not in response.json
seeder.authorize_api_access(user_id, admin_unit_id)
response = utils.get_json(url)
response = utils.get_json_ok(url)
assert "can_verify_other" in response.json
def test_list(client, seeder, utils):
seeder.setup_base()
def test_list(client, seeder: Seeder, utils: UtilActions):
seeder.setup_api_access(user_access=False)
url = utils.get_url("api_v1_organization_list", keyword="crew")
utils.get_ok(url)
utils.get_json_ok(url)
def test_list_unverified(client, app, seeder, utils):
_, verified_admin_unit_id = seeder.setup_base(
def test_list_unverified(client, app, seeder: Seeder, utils: UtilActions):
user_id, verified_admin_unit_id = seeder.setup_base(
email="verified@test.de",
log_in=False,
name="Verified",
@ -33,17 +36,18 @@ def test_list_unverified(client, app, seeder, utils):
name="Unverified",
admin_unit_verified=False,
)
seeder.grant_client_credentials_api_access(user_id)
# Unauthorisierte Nutzer sehen nur verifizierte Organisationen
url = utils.get_url("api_v1_organization_list")
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["id"] == verified_admin_unit_id
# user_id1 sieht verified_admin_unit_id, weil sie verifiziert ist,
# aber nicht unverified_admin_unit_id, weil sie nicht verifiziert ist.
utils.login("verified@test.de")
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["id"] == verified_admin_unit_id
@ -56,7 +60,7 @@ def test_list_unverified(client, app, seeder, utils):
utils.logout()
utils.login("admin@eventcally.com")
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 3
assert response.json["items"][0]["id"] == eventcally_id
assert response.json["items"][1]["id"] == unverified_admin_unit_id
@ -66,12 +70,12 @@ def test_list_unverified(client, app, seeder, utils):
seeder.create_user("admin@test.de", admin=True)
utils.logout()
utils.login("admin@test.de")
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 3
def test_event_date_search(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base(log_in=False)
def test_event_date_search(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
event_id = seeder.create_event(admin_unit_id)
seeder.create_event(admin_unit_id, draft=True)
seeder.create_event_unverified()
@ -79,7 +83,7 @@ def test_event_date_search(client, seeder, utils):
url = utils.get_url(
"api_v1_organization_event_date_search", id=admin_unit_id, sort="-rating"
)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["event"]["id"] == event_id
@ -90,14 +94,14 @@ def test_event_date_search(client, seeder, utils):
assert response.json["items"][1]["event"]["public_status"] == "draft"
def test_event_search(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base(log_in=False)
def test_event_search(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
event_id = seeder.create_event(admin_unit_id)
seeder.create_event(admin_unit_id, draft=True)
seeder.create_event_unverified()
url = utils.get_url("api_v1_organization_event_search", id=admin_unit_id)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["id"] == event_id
@ -111,17 +115,17 @@ def test_event_search(client, seeder, utils):
)
def test_organizers(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
def test_organizers(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
seeder.upsert_default_event_organizer(admin_unit_id)
url = utils.get_url(
"api_v1_organization_organizer_list", id=admin_unit_id, name="crew"
)
utils.get_ok(url)
utils.get_json_ok(url)
def test_organizers_post(client, seeder, utils, app):
def test_organizers_post(client, seeder: Seeder, utils: UtilActions, app):
user_id, admin_unit_id = seeder.setup_api_access()
url = utils.get_url("api_v1_organization_organizer_list", id=admin_unit_id)
@ -140,13 +144,13 @@ def test_organizers_post(client, seeder, utils, app):
assert organizer is not None
def test_events(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base(log_in=False)
def test_events(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
event_id = seeder.create_event(admin_unit_id)
seeder.create_event(admin_unit_id, draft=True)
url = utils.get_url("api_v1_organization_event_list", id=admin_unit_id)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["id"] == event_id
@ -178,7 +182,7 @@ def prepare_events_post_data(seeder, utils, legacy=False):
@pytest.mark.parametrize("variant", ["allday", "legacy", "two_date_definitions"])
def test_events_post(client, seeder, utils, app, variant):
def test_events_post(client, seeder: Seeder, utils: UtilActions, app, variant):
url, data, admin_unit_id, place_id, organizer_id = prepare_events_post_data(
seeder, utils, variant == "legacy"
)
@ -215,7 +219,7 @@ def test_events_post(client, seeder, utils, app, variant):
assert len(event.date_definitions) == 1
def test_events_post_co_organizers(client, seeder, utils, app, db):
def test_events_post_co_organizers(client, seeder: Seeder, utils: UtilActions, app, db):
url, data, admin_unit_id, place_id, organizer_id = prepare_events_post_data(
seeder, utils
)
@ -239,7 +243,7 @@ def test_events_post_co_organizers(client, seeder, utils, app, db):
assert event.co_organizers[1].id == organizer_b_id
def test_events_post_photo_no_data(client, seeder, utils, app):
def test_events_post_photo_no_data(client, seeder: Seeder, utils: UtilActions, app):
url, data, admin_unit_id, place_id, organizer_id = prepare_events_post_data(
seeder, utils
)
@ -252,7 +256,7 @@ def test_events_post_photo_no_data(client, seeder, utils, app):
assert error["message"] == "Either image_url or image_base64 has to be defined."
def test_events_post_photo_too_small(client, seeder, utils, app):
def test_events_post_photo_too_small(client, seeder: Seeder, utils: UtilActions, app):
url, data, admin_unit_id, place_id, organizer_id = prepare_events_post_data(
seeder, utils
)
@ -267,13 +271,15 @@ def test_events_post_photo_too_small(client, seeder, utils, app):
assert error["message"] == "Image is too small (1x1px). At least 320x320px."
def test_events_import(client, seeder, utils, app, db, shared_datadir):
def test_events_import(
client, seeder: Seeder, utils: UtilActions, app, db, shared_datadir
):
external_url = "https://www.harzinfo.de/event/xy"
utils.mock_get_request_with_file(
external_url, shared_datadir, "harzinfo_biathlon.html"
)
_, admin_unit_id = seeder.setup_base()
_, admin_unit_id = seeder.setup_api_access()
url = utils.get_url("api_v1_organization_event_import", id=admin_unit_id)
response = utils.post_json(url, {"url": external_url})
@ -294,15 +300,15 @@ def test_events_import(client, seeder, utils, app, db, shared_datadir):
utils.assert_response_unprocessable_entity(response)
def test_places(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
def test_places(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
seeder.upsert_default_event_place(admin_unit_id)
url = utils.get_url("api_v1_organization_place_list", id=admin_unit_id, name="crew")
utils.get_ok(url)
utils.get_json_ok(url)
def test_places_post(client, seeder, utils, app):
def test_places_post(client, seeder: Seeder, utils: UtilActions, app):
user_id, admin_unit_id = seeder.setup_api_access()
url = utils.get_url("api_v1_organization_place_list", id=admin_unit_id)
@ -331,19 +337,19 @@ def test_places_post(client, seeder, utils, app):
assert place.location.city == "Goslar"
def test_event_lists(client, seeder, utils):
_, admin_unit_id = seeder.setup_base()
def test_event_lists(client, seeder: Seeder, utils: UtilActions):
_, admin_unit_id = seeder.setup_api_access(user_access=False)
event_list_id = seeder.create_event_list(admin_unit_id, name="Meine Liste")
url = utils.get_url(
"api_v1_organization_event_list_list", id=admin_unit_id, name="meine"
)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["id"] == event_list_id
def test_event_lists_post(client, seeder, utils, app):
def test_event_lists_post(client, seeder: Seeder, utils: UtilActions, app):
_, admin_unit_id = seeder.setup_api_access()
url = utils.get_url("api_v1_organization_event_list_list", id=admin_unit_id)
@ -368,8 +374,8 @@ def test_event_lists_post(client, seeder, utils, app):
assert event_list.name == "Neue Liste"
def test_event_lists_status(client, seeder, utils):
_, admin_unit_id = seeder.setup_base()
def test_event_lists_status(client, seeder: Seeder, utils: UtilActions):
_, admin_unit_id = seeder.setup_api_access(user_access=False)
event_id = seeder.create_event(admin_unit_id)
event_list_id = seeder.create_event_list(
admin_unit_id, event_id, name="Meine Liste"
@ -381,14 +387,14 @@ def test_event_lists_status(client, seeder, utils):
event_id=event_id,
name="meine",
)
response = utils.get_ok(url)
response = utils.get_json_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["event_list"]["id"] == event_list_id
assert response.json["items"][0]["contains_event"]
def test_references_incoming(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
def test_references_incoming(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
(
other_user_id,
other_admin_unit_id,
@ -401,11 +407,11 @@ def test_references_incoming(client, seeder, utils):
id=admin_unit_id,
name="crew",
)
utils.get_ok(url)
utils.get_json_ok(url)
def test_references_outgoing(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
def test_references_outgoing(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
event_id = seeder.create_event(admin_unit_id)
other_user_id = seeder.create_user("other@test.de")
@ -417,13 +423,17 @@ def test_references_outgoing(client, seeder, utils):
id=admin_unit_id,
name="crew",
)
utils.get_ok(url)
utils.get_json_ok(url)
@pytest.mark.parametrize("session_based", [True, False])
def test_outgoing_relation_list(client, seeder, utils, session_based):
def test_outgoing_relation_list(
client, seeder: Seeder, utils: UtilActions, session_based
):
user_id, admin_unit_id = (
seeder.setup_base() if session_based else seeder.setup_api_access()
seeder.setup_api_access(user_access=False)
if session_based
else seeder.setup_api_access()
)
(
other_user_id,
@ -443,8 +453,10 @@ def test_outgoing_relation_list(client, seeder, utils, session_based):
assert response.json["items"][0]["target_organization"]["id"] == other_admin_unit_id
def test_outgoing_relation_list_notAuthenticated(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base(log_in=False)
def test_outgoing_relation_list_notAuthenticated(
client, seeder: Seeder, utils: UtilActions
):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
(
other_user_id,
other_admin_unit_id,
@ -459,7 +471,7 @@ def test_outgoing_relation_list_notAuthenticated(client, seeder, utils):
utils.assert_response_unauthorized(response)
def test_outgoing_relation_post(client, app, seeder, utils, db):
def test_outgoing_relation_post(client, app, seeder: Seeder, utils: UtilActions, db):
user_id, admin_unit_id = seeder.setup_api_access()
other_user_id = seeder.create_user("other@test.de")
other_admin_unit_id = seeder.create_admin_unit(other_user_id, "Other Crew")
@ -487,7 +499,9 @@ def test_outgoing_relation_post(client, app, seeder, utils, db):
assert relation.auto_verify_event_reference_requests
def test_outgoing_relation_post_unknownTarget(client, app, seeder, utils):
def test_outgoing_relation_post_unknownTarget(
client, app, seeder: Seeder, utils: UtilActions
):
user_id, admin_unit_id = seeder.setup_api_access()
url = utils.get_url(
@ -503,7 +517,9 @@ def test_outgoing_relation_post_unknownTarget(client, app, seeder, utils):
utils.assert_response_unprocessable_entity(response)
def test_outgoing_relation_post_selfReference(client, app, seeder, utils):
def test_outgoing_relation_post_selfReference(
client, app, seeder: Seeder, utils: UtilActions
):
user_id, admin_unit_id = seeder.setup_api_access()
url = utils.get_url(
@ -519,8 +535,8 @@ def test_outgoing_relation_post_selfReference(client, app, seeder, utils):
utils.assert_response_bad_request(response)
def test_organization_invitation_list(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
def test_organization_invitation_list(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access()
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
url = utils.get_url(
@ -573,17 +589,17 @@ def test_organization_invitation_list_post(client, app, seeder, db, utils, mocke
utils.assert_send_mail_called(mail_mock, "invited@test.de", invitation_url)
def test_custom_widgets(client, seeder, utils):
_, admin_unit_id = seeder.setup_base()
def test_custom_widgets(client, seeder: Seeder, utils: UtilActions):
_, admin_unit_id = seeder.setup_api_access(user_access=False)
seeder.insert_event_custom_widget(admin_unit_id)
url = utils.get_url(
"api_v1_organization_custom_widget_list", id=admin_unit_id, name="mein"
)
utils.get_ok(url)
utils.get_json_ok(url)
def test_custom_widgets_post(client, seeder, utils, app):
def test_custom_widgets_post(client, seeder: Seeder, utils: UtilActions, app):
user_id, admin_unit_id = seeder.setup_api_access()
url = utils.get_url("api_v1_organization_custom_widget_list", id=admin_unit_id)

View File

@ -1,12 +1,16 @@
def test_read(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
from tests.seeder import Seeder
from tests.utils import UtilActions
def test_read(client, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
organizer_id = seeder.upsert_default_event_organizer(admin_unit_id)
url = utils.get_url("api_v1_organizer", id=organizer_id)
utils.get_ok(url)
utils.get_json_ok(url)
def test_put(client, seeder, utils, app, db):
def test_put(client, seeder: Seeder, utils: UtilActions, app, db):
user_id, admin_unit_id = seeder.setup_api_access()
organizer_id = seeder.upsert_default_event_organizer(admin_unit_id)
@ -21,7 +25,7 @@ def test_put(client, seeder, utils, app, db):
assert organizer.name == "Neuer Name"
def test_patch(client, seeder, utils, app, db):
def test_patch(client, seeder: Seeder, utils: UtilActions, app, db):
user_id, admin_unit_id = seeder.setup_api_access()
organizer_id = seeder.upsert_default_event_organizer(admin_unit_id)
@ -37,7 +41,7 @@ def test_patch(client, seeder, utils, app, db):
assert organizer.phone == "55555"
def test_delete(client, seeder, utils, app, db):
def test_delete(client, seeder: Seeder, utils: UtilActions, app, db):
user_id, admin_unit_id = seeder.setup_api_access()
organizer_id = seeder.upsert_default_event_organizer(admin_unit_id)

View File

@ -1,12 +1,16 @@
def test_read(client, app, db, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
from tests.seeder import Seeder
from tests.utils import UtilActions
def test_read(client, app, db, seeder: Seeder, utils: UtilActions):
user_id, admin_unit_id = seeder.setup_api_access(user_access=False)
place_id = seeder.upsert_default_event_place(admin_unit_id)
url = utils.get_url("api_v1_place", id=place_id)
utils.get_ok(url)
utils.get_json_ok(url)
def test_put(client, seeder, utils, app, db):
def test_put(client, seeder: Seeder, utils: UtilActions, app, db):
user_id, admin_unit_id = seeder.setup_api_access()
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -37,7 +41,7 @@ def test_put_nonActiveReturnsUnauthorized(client, seeder, db, utils, app):
utils.assert_response_unauthorized(response)
def test_patch(client, seeder, utils, app, db):
def test_patch(client, seeder: Seeder, utils: UtilActions, app, db):
user_id, admin_unit_id = seeder.setup_api_access()
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -53,7 +57,7 @@ def test_patch(client, seeder, utils, app, db):
assert place.description == "Klasse"
def test_patch_location(db, seeder, utils, app):
def test_patch_location(db, seeder: Seeder, utils: UtilActions, app):
user_id, admin_unit_id = seeder.setup_api_access()
place_id = seeder.upsert_default_event_place(admin_unit_id)
@ -85,7 +89,7 @@ def test_patch_location(db, seeder, utils, app):
assert place.location.postalCode == "54321"
def test_delete(client, seeder, utils, app, db):
def test_delete(client, seeder: Seeder, utils: UtilActions, app, db):
user_id, admin_unit_id = seeder.setup_api_access()
place_id = seeder.upsert_default_event_place(admin_unit_id)

View File

@ -1,5 +1,5 @@
def test_organization_invitation_list(client, seeder, utils):
_, admin_unit_id = seeder.setup_base(log_in=False)
_, admin_unit_id = seeder.setup_api_access(user_access=False)
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
seeder.create_user("invited@test.de")
@ -15,7 +15,7 @@ def test_organization_invitation_list(client, seeder, utils):
def test_organization_invitation_read(client, seeder, utils):
_, admin_unit_id = seeder.setup_base(log_in=False)
_, admin_unit_id = seeder.setup_api_access(user_access=False)
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
seeder.create_user("invited@test.de")
@ -33,7 +33,7 @@ def test_organization_invitation_read(client, seeder, utils):
def test_organization_invitation_read_wrongEmail(client, seeder, utils):
_, admin_unit_id = seeder.setup_base(log_in=False)
_, admin_unit_id = seeder.setup_api_access(user_access=False)
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
seeder.create_user("other@test.de")
@ -45,7 +45,7 @@ def test_organization_invitation_read_wrongEmail(client, seeder, utils):
def test_organization_invitation_delete(client, app, seeder, utils, db):
_, admin_unit_id = seeder.setup_base(log_in=False)
_, admin_unit_id = seeder.setup_api_access(user_access=False)
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
seeder.create_user("invited@test.de")

View File

@ -33,6 +33,7 @@ def app():
app.config["SERVER_NAME"] = None
app.config["TESTING"] = True
app.config["ADMIN_UNIT_CREATE_REQUIRES_ADMIN"] = False
app.config["API_READ_ANONYM"] = False
app.testing = True
return app

View File

@ -1,5 +1,8 @@
from tests.utils import UtilActions
class Seeder(object):
def __init__(self, app, db, utils):
def __init__(self, app, db, utils: UtilActions):
self._app = app
self._db = db
self._utils = utils
@ -247,6 +250,25 @@ class Seeder(object):
return organizer_id
def upsert_admin_unit_api_key(self, admin_unit_id, name):
from project.services.api_key import upsert_api_key
with self._app.app_context():
api_key = upsert_api_key(admin_unit_id, name)
self._db.session.commit()
api_key_id = api_key.id
return api_key_id
def upsert_default_admin_unit_api_key(self, admin_unit_id):
from project.services.admin_unit import get_admin_unit_by_id
with self._app.app_context():
admin_unit = get_admin_unit_by_id(admin_unit_id)
api_key_id = self.upsert_admin_unit_api_key(admin_unit_id, admin_unit.name)
return api_key_id
def insert_event_custom_widget(
self,
admin_unit_id,
@ -282,9 +304,6 @@ class Seeder(object):
metadata = dict()
metadata["client_name"] = "Mein Client"
metadata["scope"] = " ".join(scope_list)
metadata["grant_types"] = ["authorization_code", "refresh_token"]
metadata["response_types"] = ["code"]
metadata["token_endpoint_auth_method"] = "client_secret_post"
metadata["redirect_uris"] = [self._utils.get_url("swagger_oauth2_redirect")]
client.set_client_metadata(metadata)
@ -294,11 +313,17 @@ class Seeder(object):
return client_id
def setup_api_access(self, admin=True, admin_unit_verified=True):
def setup_api_access(self, admin=True, admin_unit_verified=True, user_access=True):
user_id, admin_unit_id = self.setup_base(
admin=admin, log_in=False, admin_unit_verified=admin_unit_verified
)
return self.authorize_api_access(user_id, admin_unit_id)
if user_access:
self.authorize_api_access(user_id, admin_unit_id)
else:
self.grant_client_credentials_api_access(user_id)
return (user_id, admin_unit_id)
def authorize_api_access(self, user_id, admin_unit_id):
oauth2_client_id = self.insert_default_oauth2_client(user_id)
@ -316,6 +341,19 @@ class Seeder(object):
self._utils.logout()
return (user_id, admin_unit_id)
def grant_client_credentials_api_access(self, user_id):
oauth2_client_id = self.insert_default_oauth2_client(user_id)
with self._app.app_context():
from project.models import OAuth2Client
oauth2_client = self._db.session.get(OAuth2Client, oauth2_client_id)
client_id = oauth2_client.client_id
client_secret = oauth2_client.client_secret
scope = oauth2_client.scope
self._utils.grant_client_credentials(client_id, client_secret, scope)
def get_event_category_id(self, category_name):
from project.services.event import get_event_category

View File

@ -105,7 +105,7 @@ class UtilActions(object):
)
def get_ajax_csrf(self, response):
pattern = 'var csrf_token = "(.*)";'
pattern = r"xhr\.setRequestHeader\(\"X-CSRFToken\", \"(.*)\"\);"
match = re.search(pattern.encode("utf-8"), response.data)
if not match:
@ -161,6 +161,11 @@ class UtilActions(object):
self.log_response(response)
return response
def get_json_ok(self, url):
response = self.get_json(url)
self.assert_response_ok(response)
return response
def post_json(self, url, data: dict):
self.log_json_request(url, data)
response = self._client.post(url, json=data, headers=self.get_headers())
@ -374,30 +379,47 @@ class UtilActions(object):
code = params["code"]
# Mit dem Code den Access-Token abfragen
token_url = self.get_url("issue_token")
response = self.post_form_data(
token_url,
data={
self._get_token(
client_id, client_secret, scope, "authorization_code", code, redirect_uri
)
def grant_client_credentials(self, client_id, client_secret, scope):
# Mit den Credentials den Access-Token abfragen
self._get_token(client_id, client_secret, scope, "client_credentials")
def _get_token(
self, client_id, client_secret, scope, grant_type, code=None, redirect_uri=None
):
data = {
"client_id": client_id,
"client_secret": client_secret,
"grant_type": "authorization_code",
"grant_type": grant_type,
"scope": scope,
"code": code,
"redirect_uri": redirect_uri,
},
)
}
if grant_type == "authorization_code":
data["code"] = code
data["redirect_uri"] = redirect_uri
token_url = self.get_url("issue_token")
response = self.post_form_data(token_url, data=data)
self.assert_response_ok(response)
assert response.content_type == "application/json"
assert "access_token" in response.json
assert "expires_in" in response.json
if grant_type == "authorization_code":
assert "refresh_token" in response.json
assert response.json["scope"] == scope
assert response.json["token_type"] == "Bearer"
self._client_id = client_id
self._client_secret = client_secret
self._access_token = response.json["access_token"]
if grant_type == "authorization_code":
self._refresh_token = response.json["refresh_token"]
def refresh_token(self):

View File

@ -0,0 +1,10 @@
from tests.seeder import Seeder
from tests.utils import UtilActions
def test_custom_widget_type(client, seeder: Seeder, utils: UtilActions):
_, admin_unit_id = seeder.setup_base()
custom_widget_id = seeder.insert_event_custom_widget(admin_unit_id)
url = utils.get_url("custom_widget_type", type="search", id=custom_widget_id)
utils.get_ok(url)

View File

@ -2,7 +2,7 @@ from tests.seeder import Seeder
from tests.utils import UtilActions
def test_js_check_org_short_name(client, seeder, utils):
def test_js_check_org_short_name(client, seeder, utils: UtilActions):
seeder.create_user(admin=True)
utils.login()
@ -210,3 +210,11 @@ def test_js_autocomplete_gmaps_place(client, seeder: Seeder, utils: UtilActions)
utils.assert_response_ok(response)
assert response.json["place_id"] == "123"
def test_js_widget_loader_custom_widget(client, seeder: Seeder, utils: UtilActions):
_, admin_unit_id = seeder.setup_base(log_in=False)
custom_widget_id = seeder.insert_event_custom_widget(admin_unit_id)
url = utils.get_url("js_widget_loader_custom_widget", id=custom_widget_id)
utils.get_ok(url)

View File

@ -1,7 +1,14 @@
def test_organizations(client, seeder, utils):
from tests.seeder import Seeder
from tests.utils import UtilActions
def test_organizations(client, seeder: Seeder, utils: UtilActions):
url = utils.get_url("organizations")
utils.get_ok(url)
url = utils.get_url("api_v1_organization_list")
utils.get_json_ok(url)
def test_ical(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base(log_in=False)