forked from fossasia/open-event-server
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaccess_codes.py
More file actions
201 lines (178 loc) · 6.65 KB
/
access_codes.py
File metadata and controls
201 lines (178 loc) · 6.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
from flask_rest_jsonapi import ResourceDetail, ResourceList, ResourceRelationship
from flask_rest_jsonapi.exceptions import ObjectNotFound
from sqlalchemy.orm.exc import NoResultFound
from app.api.bootstrap import api
from app.api.helpers.db import safe_query, safe_query_kwargs
from app.api.helpers.errors import ConflictError, ForbiddenError, UnprocessableEntityError
from app.api.helpers.permission_manager import has_access
from app.api.helpers.permissions import jwt_required
from app.api.helpers.query import event_query
from app.api.helpers.utilities import require_relationship
from app.api.schema.access_codes import AccessCodeSchema
from app.models import db
from app.models.access_code import AccessCode
from app.models.event import Event
from app.models.ticket import Ticket
from app.models.user import User
class AccessCodeListPost(ResourceList):
"""
Create AccessCodes
"""
def before_post(self, args, kwargs, data):
"""
before post method to check for required relationships and permissions
:param args:
:param kwargs:
:param data:
:return:
"""
require_relationship(['event', 'user'], data)
if not has_access('is_coorganizer', event_id=data['event']):
raise ForbiddenError({'source': ''}, "Minimum Organizer access required")
def before_create_object(self, data, view_kwargs):
"""
before create object method for AccessCodeListPost Class
:param data:
:param view_kwargs:
:return:
"""
if data.get('tickets', None):
for ticket in data['tickets']:
# Ensuring that the ticket exists and is hidden.
try:
ticket_object = (
self.session.query(Ticket)
.filter_by(id=int(ticket), deleted_at=None)
.one()
)
if not ticket_object.is_hidden:
raise ConflictError(
{'pointer': '/data/relationships/tickets'},
f"Ticket with id {ticket} is public."
+ " Access code cannot be applied to public tickets",
)
except NoResultFound:
raise ConflictError(
{'pointer': '/data/relationships/tickets'},
"Ticket with id {} does not exists".format(str(ticket)),
)
schema = AccessCodeSchema
methods = [
'POST',
]
data_layer = {
'session': db.session,
'model': AccessCode,
'methods': {'before_create_object': before_create_object},
}
class AccessCodeList(ResourceList):
"""
List AccessCodes
"""
def query(self, view_kwargs):
"""
Method to get access codes list based on different view_kwargs
:param view_kwargs:
:return:
"""
query_ = self.session.query(AccessCode)
query_ = event_query(query_, view_kwargs, permission='is_coorganizer')
if view_kwargs.get('user_id'):
user = safe_query_kwargs(User, view_kwargs, 'user_id')
if not has_access('is_user_itself', user_id=user.id):
raise ForbiddenError({'source': ''}, 'Access Forbidden')
query_ = query_.join(User).filter(User.id == user.id)
if view_kwargs.get('ticket_id'):
ticket = safe_query_kwargs(Ticket, view_kwargs, 'ticket_id')
if not has_access('is_coorganizer', event_id=ticket.event_id):
raise ForbiddenError({'source': ''}, 'Access Forbidden')
# access_code - ticket :: many-to-many relationship
query_ = AccessCode.query.filter(AccessCode.tickets.any(id=ticket.id))
query_
return query_
view_kwargs = True
methods = [
'GET',
]
schema = AccessCodeSchema
data_layer = {
'session': db.session,
'model': AccessCode,
'methods': {'query': query,},
}
class AccessCodeDetail(ResourceDetail):
"""
AccessCode detail by id or code
"""
def before_get(self, args, kwargs):
"""
before get method of access code details.
Check for permissions on the basis of kwargs.
:param args:
:param kwargs:
:return:
"""
# Any user can fetch access code details using the code.
if kwargs.get('access_event_identifier'):
event = safe_query(
Event,
'identifier',
kwargs['access_event_identifier'],
'event_identifier',
)
kwargs['access_event_id'] = event.id
if kwargs.get('code') and kwargs.get('access_event_id'):
access = (
db.session.query(AccessCode)
.filter_by(
code=kwargs.get('code'), event_id=kwargs.get('access_event_id')
)
.first()
)
if access:
kwargs['id'] = access.id
else:
raise ObjectNotFound({'parameter': '{code}'}, "Access Code: not found")
return
# Co-organizer or the admin can fetch access code details using the id.
if kwargs.get('id'):
access = db.session.query(AccessCode).filter_by(id=kwargs.get('id')).one()
if not access:
raise ObjectNotFound({'parameter': '{id}'}, "Access Code: not found")
if not has_access('is_coorganizer', event_id=access.event_id):
raise UnprocessableEntityError(
{'source': ''}, "Please verify your permission"
)
decorators = (
api.has_permission(
'is_coorganizer',
fetch='event_id',
fetch_as="event_id",
model=AccessCode,
methods="PATCH",
),
api.has_permission(
'is_coorganizer_but_not_admin',
fetch='event_id',
fetch_as="event_id",
model=AccessCode,
methods="DELETE",
),
)
schema = AccessCodeSchema
data_layer = {'session': db.session, 'model': AccessCode}
class AccessCodeRelationshipRequired(ResourceRelationship):
"""
AccessCode Relationship Required
"""
decorators = (jwt_required,)
methods = ['GET', 'PATCH']
schema = AccessCodeSchema
data_layer = {'session': db.session, 'model': AccessCode}
class AccessCodeRelationshipOptional(ResourceRelationship):
"""
AccessCode Relationship Optional
"""
decorators = (jwt_required,)
schema = AccessCodeSchema
data_layer = {'session': db.session, 'model': AccessCode}