def _getParams(self): super(NoteExportHook, self)._getParams() event = self._obj = Event.get(self._pathParams['event_id'], is_deleted=False) if event is None: raise HTTPAPIError('No such event', 404) session_id = self._pathParams.get('session_id') if session_id: self._obj = Session.query.with_parent(event).filter_by( id=session_id).first() if self._obj is None: raise HTTPAPIError("No such session", 404) contribution_id = self._pathParams.get('contribution_id') if contribution_id: contribution = self._obj = ( Contribution.query.with_parent(event).filter_by( id=contribution_id, is_deleted=False).first()) if contribution is None: raise HTTPAPIError("No such contribution", 404) subcontribution_id = self._pathParams.get('subcontribution_id') if subcontribution_id: self._obj = SubContribution.query.with_parent( contribution).filter_by(id=subcontribution_id, is_deleted=False).first() if self._obj is None: raise HTTPAPIError("No such subcontribution", 404) self._note = EventNote.get_for_linked_object(self._obj, preload_event=False) if self._note is None or self._note.is_deleted: raise HTTPAPIError("No such note", 404)
def __call__(self, user): """Perform the actual exporting""" if self.HTTP_POST != (request.method == 'POST'): raise HTTPAPIError('This action requires %s' % ('POST' if self.HTTP_POST else 'GET'), 405) if not self.GUEST_ALLOWED and not user: raise HTTPAPIError('Guest access to this resource is forbidden.', 403) method_name = self._getMethodName() func = getattr(self, method_name, None) extra_func = getattr(self, method_name + '_extra', None) if not func: raise NotImplementedError(method_name) if not self.COMMIT: is_response, resultList, complete, extra = self._perform(user, func, extra_func) db.session.rollback() else: try: init_email_queue() is_response, resultList, complete, extra = self._perform(user, func, extra_func) db.session.commit() flush_email_queue() except Exception: db.session.rollback() raise if is_response: return resultList return resultList, extra, complete, self.SERIALIZER_TYPE_MAP
def export_user(self, user): if not user: raise HTTPAPIError('You need to be logged in', 403) user = User.get(self._user_id, is_deleted=False) if not user: raise HTTPAPIError('Requested user not found', 404) if not user.can_be_modified(user): raise HTTPAPIError('You do not have access to that info', 403) return [user.as_avatar.fossilize()]
def _getParams(self): super(AgreementExportHook, self)._getParams() type_ = self._pathParams['agreement_type'] try: self._definition = get_agreement_definitions()[type_] except KeyError: raise HTTPAPIError('No such agreement type', 404) self.event = Event.get(self._pathParams['event_id'], is_deleted=False) if self.event is None: raise HTTPAPIError('No such event', 404)
def _getParams(self): self._offset = get_query_parameter(self._queryParams, ['O', 'offset'], 0, integer=True) if self._offset < 0: raise HTTPAPIError('Offset must be a positive number', 400) self._orderBy = get_query_parameter(self._queryParams, ['o', 'order']) self._descending = get_query_parameter(self._queryParams, ['c', 'descending'], 'no') == 'yes' self._detail = get_query_parameter(self._queryParams, ['d', 'detail'], self.DEFAULT_DETAIL) tzName = get_query_parameter(self._queryParams, ['tz'], None) if tzName is None: tzName = config.DEFAULT_TIMEZONE try: self._tz = pytz.timezone(tzName) except pytz.UnknownTimeZoneError, e: raise HTTPAPIError("Bad timezone: '%s'" % e.message, 400)
def _getParams(self): super(FileHook, self)._getParams() self._attachment = Attachment.get(int(self._pathParams['res'])) if not self._attachment: raise HTTPAPIError("File not found", 404)
def _perform(self, user, func, extra_func): self._getParams() if not self._has_access(user): raise HTTPAPIError('Access to this resource is restricted.', 403) resultList, complete = self._performCall(func, user) if isinstance(resultList, current_app.response_class): return True, resultList, None, None extra = extra_func(user, resultList) if extra_func else None return False, resultList, complete, extra
def _has_access(self, user): if not config.ENABLE_ROOMBOOKING or not rb_check_user_access(user): return False if self._room.can_be_booked(user): return True elif self._room.can_be_prebooked(user): raise HTTPAPIError( 'The API only supports direct bookings but this room only allows pre-bookings.' ) return False
def __init__(self, user, hook): super(CategoryEventFetcher, self).__init__(user, hook) self._eventType = hook._eventType self._occurrences = hook._occurrences self._location = hook._location self._room = hook._room self.user = user self._detail_level = get_query_parameter(request.args.to_dict(), ['d', 'detail'], 'events') if self._detail_level not in ('events', 'contributions', 'subcontributions', 'sessions'): raise HTTPAPIError( 'Invalid detail level: {}'.format(self._detail_level), 400)
def api_roomBooking(self, user): data = MultiDict({ 'start_dt': self._params['from'], 'end_dt': self._params['to'], 'repeat_frequency': RepeatFrequency.NEVER, 'repeat_interval': 0, 'room_id': self._room.id, 'booked_for_user': self._params['booked_for'], 'contact_email': self._params['booked_for'].email, 'contact_phone': self._params['booked_for'].phone, 'booking_reason': self._params['reason'] }) try: reservation = Reservation.create_from_data(self._room, data, user) except ConflictingOccurrences: raise HTTPAPIError( 'Failed to create the booking due to conflicts with other bookings' ) except fossirError as e: raise HTTPAPIError('Failed to create the booking: {}'.format(e)) db.session.add(reservation) db.session.flush() return {'reservationID': reservation.id}
def _getParams(self): super(AttachmentsExportHook, self)._getParams() event = self._obj = Event.get(self._pathParams['event_id'], is_deleted=False) if event is None: raise HTTPAPIError('No such event', 404) session_id = self._pathParams.get('session_id') if session_id: self._obj = Session.query.with_parent(event).filter_by( id=session_id).first() if self._obj is None: raise HTTPAPIError("No such session", 404) contribution_id = self._pathParams.get('contribution_id') if contribution_id: contribution = self._obj = Contribution.query.with_parent( event).filter_by(id=contribution_id).first() if contribution is None: raise HTTPAPIError("No such contribution", 404) subcontribution_id = self._pathParams.get('subcontribution_id') if subcontribution_id: self._obj = SubContribution.query.with_parent( contribution).filter_by(id=subcontribution_id).first() if self._obj is None: raise HTTPAPIError("No such subcontribution", 404)
def _getParams(self): super(BookRoomHook, self)._getParams() self._fromDT = utc_to_server(self._fromDT.astimezone( pytz.utc)).replace(tzinfo=None) if self._fromDT else None self._toDT = utc_to_server(self._toDT.astimezone(pytz.utc)).replace( tzinfo=None) if self._toDT else None if not self._fromDT or not self._toDT or self._fromDT.date( ) != self._toDT.date(): raise HTTPAPIError('from/to must be on the same day') elif self._fromDT >= self._toDT: raise HTTPAPIError('to must be after from') elif self._fromDT < datetime.now(): raise HTTPAPIError('You cannot make bookings in the past') username = get_query_parameter(self._queryParams, 'username') if not username: raise HTTPAPIError('No username provided') users = User.find_all(~User.is_deleted, Identity.identifier == username) if not users: raise HTTPAPIError('Username does not exist') elif len(users) != 1: raise HTTPAPIError('Ambiguous username ({} users found)'.format( len(users))) user = users[0] self._params = { 'room_id': get_query_parameter(self._queryParams, 'roomid'), 'reason': get_query_parameter(self._queryParams, 'reason'), 'booked_for': user, 'from': self._fromDT, 'to': self._toDT } missing = [key for key, val in self._params.iteritems() if not val] if missing: raise HTTPAPIError('Required params missing: {}'.format( ', '.join(missing))) self._room = Room.get(self._params['room_id']) if not self._room: raise HTTPAPIError('A room with this ID does not exist')
def category(self, idlist, format): try: idlist = map(int, idlist) except ValueError: raise HTTPAPIError('Category IDs must be numeric', 400) if format == 'ics': buf = serialize_categories_ical(idlist, self.user, event_filter=Event.happens_between( self._fromDT, self._toDT), event_filter_fn=self._filter_event, update_query=self._update_query) return send_file('events.ics', buf, 'text/calendar') else: query = (Event.query.filter( ~Event.is_deleted, Event.category_chain_overlaps(idlist), Event.happens_between(self._fromDT, self._toDT)).options( *self._get_query_options(self._detail_level))) query = self._update_query(query) return self.serialize_events( x for x in query if self._filter_event(x) and x.can_access(self.user))
class HTTPAPIHook(object): """This class is the hook between the query (path+params) and the generator of the results (fossil). It is also in charge of checking the parameters and the access rights. """ HOOK_LIST = [] TYPES = None # abstract PREFIX = 'export' # url prefix. must exist in fossir.web.flask.blueprints.api, too! also used as function prefix RE = None # abstract METHOD_NAME = None # overrides method name derived from prefix+type DEFAULT_DETAIL = None # abstract MAX_RECORDS = {} SERIALIZER_TYPE_MAP = {} # maps fossil type names to friendly names (useful for plugins e.g. RoomCERN --> Room) VALID_FORMATS = None # None = all formats GUEST_ALLOWED = True # When False, it forces authentication COMMIT = False # commit database changes HTTP_POST = False # require (and allow) HTTP POST NO_CACHE = False @classmethod def parseRequest(cls, path, queryParams): """Parse a request path and return a hook and the requested data type.""" path = urllib.unquote(path) hooks = cls.HOOK_LIST for expCls in hooks: Logger.get('HTTPAPIHook.parseRequest').debug(expCls) m = expCls._matchPath(path) if m: gd = m.groupdict() g = m.groups() type = g[0] format = g[-1] if format not in DataFetcher.getAllowedFormats(): return None, None elif expCls.VALID_FORMATS and format not in expCls.VALID_FORMATS: return None, None return expCls(queryParams, type, gd, format), format return None, None @staticmethod def register(cls): """Register a hook. To use it, simply decorate the hook class with this method.""" assert cls.RE is not None HTTPAPIHook.HOOK_LIST.append(cls) return cls @classmethod def _matchPath(cls, path): if not hasattr(cls, '_RE'): types = '|'.join(cls.TYPES) cls._RE = re.compile(r'/' + cls.PREFIX + '/(' + types + r')' + ('/' + cls.RE).rstrip('/') + r'\.(\w+)$') return cls._RE.match(path) def __init__(self, queryParams, type, pathParams, format): self._format = format self._queryParams = queryParams self._type = type self._pathParams = pathParams def _getParams(self): self._offset = get_query_parameter(self._queryParams, ['O', 'offset'], 0, integer=True) if self._offset < 0: raise HTTPAPIError('Offset must be a positive number', 400) self._orderBy = get_query_parameter(self._queryParams, ['o', 'order']) self._descending = get_query_parameter(self._queryParams, ['c', 'descending'], 'no') == 'yes' self._detail = get_query_parameter(self._queryParams, ['d', 'detail'], self.DEFAULT_DETAIL) tzName = get_query_parameter(self._queryParams, ['tz'], None) if tzName is None: tzName = config.DEFAULT_TIMEZONE try: self._tz = pytz.timezone(tzName) except pytz.UnknownTimeZoneError, e: raise HTTPAPIError("Bad timezone: '%s'" % e.message, 400) max = self.MAX_RECORDS.get(self._detail, 1000) self._userLimit = get_query_parameter(self._queryParams, ['n', 'limit'], 0, integer=True) if self._userLimit > max: raise HTTPAPIError("You can only request up to %d records per request with the detail level '%s'" % (max, self._detail), 400) self._limit = self._userLimit if self._userLimit > 0 else max fromDT = get_query_parameter(self._queryParams, ['f', 'from']) toDT = get_query_parameter(self._queryParams, ['t', 'to']) dayDT = get_query_parameter(self._queryParams, ['day']) if (fromDT or toDT) and dayDT: raise HTTPAPIError("'day' can only be used without 'from' and 'to'", 400) elif dayDT: fromDT = toDT = dayDT self._fromDT = DataFetcher._getDateTime('from', fromDT, self._tz) if fromDT else None self._toDT = DataFetcher._getDateTime('to', toDT, self._tz, aux=self._fromDT) if toDT else None
def _getDateTime(cls, ctx, dateTime, tz, aux=None): try: rel, value = cls._parseDateTime(dateTime, ctx == 'from') except ArgumentParseError, e: raise HTTPAPIError(e.message, 400)
def _getParams(self): super(RoomHook, self)._getParams() self._location = self._pathParams['location'] self._ids = map(int, self._pathParams['idlist'].split('-')) if self._detail not in {'rooms', 'reservations'}: raise HTTPAPIError('Invalid detail level: %s' % self._detail, 400)
def export_file(self, user): if self._attachment.type != AttachmentType.file: raise HTTPAPIError("Resource is not a file", 404) return self._attachment.file.send()