class SiaNode(Schema): id = UUID(title="id", description="Sia unique identifier.", required=True, dump_only=True) accepting_contracts = Boolean( title="accepting_contracts", description="True if the host is accepting new contracts.") max_download_batch_size = Integer( title="max_download_batch_size", description= "Maximum number of bytes that the host will allow to be requested by a single download request", ) max_duration = Integer( title="max_duration", description= "Maximum duration in blocks that a host will allow for a file contract. The host commits to " "keeping files for the full duration under the threat of facing a large penalty for losing or " "dropping data before the duration is complete. The storage proof window of an incoming file " "contract must end before the current height + maxduration.", ) max_revise_batch_size = Integer( title="max_revise_batch_size", description= "Maximum size in bytes of a single batch of file contract revisions. Larger batch sizes allow for " "higher throughput as there is significant communication overhead associated with performing a " "batch upload.", ) net_address = String( title="net_address", description= "Remote address of the host. It can be an IPv4, IPv6, or hostname, along with the port. IPv6 " "addresses are enclosed in square brackets.", ) remaining_storage = Integer( title="remaining_storage", description="Unused storage capacity the host claims it has.") sector_size = Integer( title="sector_size", description= "Smallest amount of data in bytes that can be uploaded or downloaded to or from the host.", ) total_storage = Integer( title="total_storage", description="Total amount of storage capacity the host claims it has.") unlock_hash = String( title="unlock_hash", description= "Address at which the host can be paid when forming file contracts.") window_size = Integer( title="window_size", description= "A storage proof window is the number of blocks that the host has to get a storage proof onto the " "blockchain. The window size is the minimum size of window that the host will accept in a file " "contract.", ) collateral = String( title="collateral", description= "The maximum amount of money that the host will put up as collateral for storage that is contracted" " by the renter.", ) max_collateral = String( title="max_collateral", description= "The maximum amount of collateral that the host will put into a single file contract.", ) contract_price = String( title="contract_price", description= "The price that a renter has to pay to create a contract with the host. The payment is intended " "to cover transaction fees for the file contract revision and the storage proof that the host will " "be submitting to the blockchain.", ) download_bandwidth_price = String( title="download_bandwidth_price", description= "The price that a renter has to pay when downloading data from the host.", ) storage_price = String( title="storage_price", description= "The price that a renter has to pay to store files with the host.") upload_bandwidth_price = String( title="upload_bandwidth_price", description= "The price that a renter has to pay when uploading data to the host.", ) revision_number = Integer( title="revision_number", description= "The revision number indicates to the renter what iteration of settings the host is currently at. " "Settings are generally signed. If the renter has multiple conflicting copies of settings from " "the host, the renter can expect the one with the higher revision number to be more recent.", ) version = String(title="version", description="The version of the host.") first_seen = Integer( title="first_seen", description= "Firstseen is the last block height at which this host was announced.") historic_downtime = Integer( title="historic_downtime", description="Total amount of time the host has been offline.") historic_uptime = Integer( title="historic_uptime", description="Total amount of time the host has been online.") historic_failed_interactions = Integer( title="historic_failed_interactions", description="Number of historic failed interactions with the host.") historic_successful_interactions = Integer( title="historic_successful_interactions", description="Number of historic successful interactions with the host.", ) recent_failed_interactions = Integer( title="recent_failed_interactions", description="Number of recent failed interactions with the host.") recent_successful_interactions = Integer( title="recent_successful_interactions", description="Number of recent successful interactions with the host.") last_historic_update = Integer( title="last_historic_update", description= "The last time that the interactions within scanhistory have been compressed into the historic " "ones.", ) public_key_string = String( title="public_key_string", description= "The string representation of the full public key, used when calling /hostdb/hosts.", ) ipnets = List( String(), title="ipnets", description= "List of IP subnet masks used by the host. For IPv4 the /24 and for IPv6 the /54 subnet mask is " "used. A host can have either one IPv4 or one IPv6 subnet or one of each. E.g. these lists are " 'valid: [ "IPv4" ], [ "IPv6" ] or [ "IPv4", "IPv6" ]. The following lists are invalid: [ "IPv4", ' '"IPv4" ], [ "IPv4", "IPv6", "IPv6" ]. Hosts with an invalid list are ignored.', ) address = String(title="address", description="The full address of the node.") country = String(title="country", description="The country where the node can be found.") city = String(title="city", description="The city where the node can be found.") latitude = Float(title="latitude", description="Geolocation latitude coordinate") longitude = Float(title="longitude", description="Geolocation longitude coordinate")
class _Schema(Schema): registration_number = String(required=True) name = String(required=True, allow_none=False) age = Integer()
class _Schema(Schema): code = String(required=True) name = String(required=True) credit_hours = Integer() has_labs = Boolean(missing=True)
class ReservationEventDataSchema(Schema): id = Number() title = String() url = String() can_access = Function(lambda event: event.can_access(session.user))
from pyramid.security import NO_PERMISSION_REQUIRED from pyramid.view import view_config from webargs.pyramidparser import use_kwargs from tildes.views.decorators import rate_limit_view @view_config( route_name="donate_stripe", request_method="POST", renderer="donate_stripe.jinja2", permission=NO_PERMISSION_REQUIRED, require_csrf=False, ) @use_kwargs({ "stripe_token": String(required=True), "donator_email": Email(required=True), "amount": Float(required=True, validate=Range(min=1.0)), "currency": String(required=True, validate=OneOf(("CAD", "USD"))), }) @rate_limit_view("donate") def post_donate_stripe(request: Request, stripe_token: str, donator_email: str, amount: int, currency: str) -> dict: """Process a Stripe donation.""" try: stripe.api_key = request.registry.settings["api_keys.stripe"] except KeyError: raise HTTPInternalServerError payment_successful = True try:
class ProfileForm(Schema): username = String(required=True, allow_none=False, validate=[ Length(min=1, error=_('Username is required')), Regexp(r'^[a-zA-Z_\-0-9]+$', error=_('Username cannot contain space and can only contain a-z,_,0-9')) ], error_messages={ 'required': _('Username is required'), 'null': _('Username is required') } ) password = String(required=False, allow_none=True, validate=[ Length(min=1, error=_('Password is required')) ], error_messages={ 'required': _('Password is required'), 'null': _('Password is required') } ) confirm_password = String(required=False, allow_none=True, validate=[ Length(min=1, error=_('Confirm Password is required')) ], error_messages={ 'required': _('Confirm Password is required'), 'null': _('Confirm Password is required') } ) name = String(required=True, allow_none=False, validate=[ Length(min=1, error=_('Name is required')) ], error_messages={ 'required': _('Name is required'), 'null': _('Name is required') } ) email = String(required=True, allow_none=False, validate=[ Length(min=1, error=_('Email is required')), Email(error=_('Invalid email format')) ], error_messages={ 'required': _('Email is required'), 'null': _('Email is required') } ) class Meta: unknown = EXCLUDE @validates('username') def validate_username(self, username): user = self.context.get('user') connect_db() check = g.db.execute( sa.select([User.c.id]).where( sa.and_( User.c.username == username, User.c.id != user.id ) ) ).scalar() if check: raise ValidationError(_('Username %(username)s already exists. Please input another username.', username=username)) @validates_schema(skip_on_field_errors=True) def validate_confirm_password(self, data, **kwargs): password = data.get('password') confirm_password = data.get('confirm_password') if password and not confirm_password: raise ValidationError(_('Confirm password is required'), 'confirm_password') if password and confirm_password and confirm_password != password: raise ValidationError(_('Confirm password does not match'), 'confirm_password') @validates('email') def validate_email(self, email): user = self.context.get('user') connect_db() check = g.db.execute( sa.select([User.c.id]).where( sa.and_( User.c.email == email, User.c.id != user.id ) ) ).scalar() if check: raise ValidationError(_('Email %(email)s already exists. Please input another email.', email=email))
class ChannelsEndpoint(MetadataEndpointBase): def __init__(self, download_manager: DownloadManager, gigachannel_manager: GigaChannelManager, gigachannel_community: GigaChannelCommunity, *args, **kwargs): MetadataEndpointBase.__init__(self, *args, **kwargs) self.download_manager = download_manager self.gigachannel_manager = gigachannel_manager self.gigachannel_community = gigachannel_community def setup_routes(self): self.app.add_routes( [ web.get('', self.get_channels), web.get(r'/{channel_pk:\w*}/{channel_id:\w*}', self.get_channel_contents), web.get(r'/{channel_pk:\w*}/{channel_id:\w*}/description', self.get_channel_description), web.put(r'/{channel_pk:\w*}/{channel_id:\w*}/description', self.put_channel_description), web.get(r'/{channel_pk:\w*}/{channel_id:\w*}/thumbnail', self.get_channel_thumbnail), web.put(r'/{channel_pk:\w*}/{channel_id:\w*}/thumbnail', self.put_channel_thumbnail), web.post(r'/{channel_pk:\w*}/{channel_id:\w*}/copy', self.copy_channel), web.post(r'/{channel_pk:\w*}/{channel_id:\w*}/channels', self.create_channel), web.post(r'/{channel_pk:\w*}/{channel_id:\w*}/collections', self.create_collection), web.put(r'/{channel_pk:\w*}/{channel_id:\w*}/torrents', self.add_torrent_to_channel), web.post(r'/{channel_pk:\w*}/{channel_id:\w*}/commit', self.post_commit), web.get(r'/{channel_pk:\w*}/{channel_id:\w*}/commit', self.is_channel_dirty), web.get('/popular_torrents', self.get_popular_torrents_channel), ] ) def add_download_progress_to_metadata_list(self, contents_list): for torrent in contents_list: if torrent['type'] == REGULAR_TORRENT: dl = self.download_manager.get_download(unhexlify(torrent['infohash'])) if dl is not None and dl.tdef.infohash not in self.download_manager.metainfo_requests: torrent['progress'] = dl.get_state().get_progress() def get_channel_from_request(self, request): channel_pk = ( self.mds.my_key.pub().key_to_bin()[10:] if request.match_info['channel_pk'] == 'mychannel' else unhexlify(request.match_info['channel_pk']) ) channel_id = int(request.match_info['channel_id']) return channel_pk, channel_id @docs( tags=['Metadata'], summary='Get a list of all channels known to the system.', responses={ 200: { 'schema': schema( GetChannelsResponse={ 'results': [ChannelSchema], 'first': Integer(), 'last': Integer(), 'sort_by': String(), 'sort_desc': Integer(), 'total': Integer(), } ) } }, ) async def get_channels(self, request): sanitized = self.sanitize_parameters(request.query) sanitized['subscribed'] = None if 'subscribed' not in request.query else bool(int(request.query['subscribed'])) include_total = request.query.get('include_total', '') sanitized.update({"origin_id": 0}) sanitized['metadata_type'] = CHANNEL_TORRENT with db_session: channels = self.mds.get_entries(**sanitized) total = self.mds.get_total_count(**sanitized) if include_total else None channels_list = [] for channel in channels: channel_dict = channel.to_simple_dict() # Add progress info for those channels that are still being processed if channel.subscribed: if channel_dict["state"] == CHANNEL_STATE.UPDATING.value: try: progress = self.mds.compute_channel_update_progress(channel) channel_dict["progress"] = progress except (ZeroDivisionError, FileNotFoundError) as e: self._logger.error( "Error %s when calculating channel update progress. Channel data: %s-%i %i/%i", e, hexlify(channel.public_key), channel.id_, channel.start_timestamp, channel.local_version, ) elif channel_dict["state"] == CHANNEL_STATE.METAINFO_LOOKUP.value: if not self.download_manager.metainfo_requests.get( bytes(channel.infohash) ) and self.download_manager.download_exists(bytes(channel.infohash)): channel_dict["state"] = CHANNEL_STATE.DOWNLOADING.value channels_list.append(channel_dict) response_dict = { "results": channels_list, "first": sanitized["first"], "last": sanitized["last"], "sort_by": sanitized["sort_by"], "sort_desc": int(sanitized["sort_desc"]), } if total is not None: response_dict.update({"total": total}) return RESTResponse(response_dict) @docs( tags=['Metadata'], summary='Get a list of the channel\'s contents (torrents/channels/etc.).', responses={ 200: { 'schema': schema( GetChannelContentsResponse={ 'results': [MetadataSchema], 'first': Integer(), 'last': Integer(), 'sort_by': String(), 'sort_desc': Integer(), 'total': Integer(), } ) } }, ) async def get_channel_contents(self, request): sanitized = self.sanitize_parameters(request.query) include_total = request.query.get('include_total', '') channel_pk, channel_id = self.get_channel_from_request(request) sanitized.update({"channel_pk": channel_pk, "origin_id": channel_id}) remote = sanitized.pop("remote", None) total = None remote_failed = False if remote: try: contents_list = await self.gigachannel_community.remote_select_channel_contents(**sanitized) except (RequestTimeoutException, NoChannelSourcesException, CancelledError): remote_failed = True if not remote or remote_failed: with db_session: contents = self.mds.get_entries(**sanitized) contents_list = [c.to_simple_dict() for c in contents] total = self.mds.get_total_count(**sanitized) if include_total else None self.add_download_progress_to_metadata_list(contents_list) self.add_tags_to_metadata_list(contents_list, hide_xxx=sanitized["hide_xxx"]) response_dict = { "results": contents_list, "first": sanitized['first'], "last": sanitized['last'], "sort_by": sanitized['sort_by'], "sort_desc": int(sanitized['sort_desc']), } if total is not None: response_dict.update({"total": total}) return RESTResponse(response_dict) async def get_channel_description(self, request): channel_pk, channel_id = self.get_channel_from_request(request) with db_session: channel_description = self.mds.ChannelDescription.select( lambda g: g.public_key == channel_pk and g.origin_id == channel_id ).first() response_dict = json.loads(channel_description.json_text) if (channel_description is not None) else {} return RESTResponse(response_dict) async def put_channel_description(self, request): channel_pk, channel_id = self.get_channel_from_request(request) request_parsed = await request.json() updated_json_text = json.dumps({"description_text": request_parsed["description_text"]}) with db_session: channel_description = self.mds.ChannelDescription.select( lambda g: g.public_key == channel_pk and g.origin_id == channel_id ).first() if channel_description is not None: channel_description.update_properties({"json_text": updated_json_text}) else: channel_description = self.mds.ChannelDescription( public_key=channel_pk, origin_id=channel_id, json_text=updated_json_text, status=NEW ) return RESTResponse(json.loads(channel_description.json_text)) async def get_channel_thumbnail(self, request): channel_pk, channel_id = self.get_channel_from_request(request) with db_session: obj = self.mds.ChannelThumbnail.select( lambda g: g.public_key == channel_pk and g.origin_id == channel_id ).first() return web.Response(body=obj.binary_data, content_type=obj.data_type) if obj else web.Response(status=400) async def put_channel_thumbnail(self, request): content_type = request.headers["Content-Type"] post_body = await request.read() channel_pk, channel_id = self.get_channel_from_request(request) obj_properties = {"binary_data": post_body, "data_type": content_type} with db_session: obj = self.mds.ChannelThumbnail.select( lambda g: g.public_key == channel_pk and g.origin_id == channel_id, ).first() if obj is not None: obj.update_properties(obj_properties) else: self.mds.ChannelThumbnail(public_key=channel_pk, origin_id=channel_id, status=NEW, **obj_properties) return web.Response(status=201) @docs( tags=['Metadata'], summary='Create a copy of an entry/entries from another channel.', parameters=[ { 'in': 'body', 'name': 'entries', 'description': 'List of entries to copy', 'example': [{'public_key': '1234567890', 'id': 123}], 'required': True, } ], responses={ 200: {'description': 'Returns a list of copied content'}, HTTP_NOT_FOUND: {'schema': HandledErrorSchema, 'example': {"error": "Target channel not found"}}, HTTP_BAD_REQUEST: {'schema': HandledErrorSchema, 'example': {"error": "Source entry not found"}}, }, ) async def copy_channel(self, request): with db_session: channel_pk, channel_id = self.get_channel_from_request(request) personal_root = channel_id == 0 and channel_pk == self.mds.my_key.pub().key_to_bin()[10:] # TODO: better error handling target_collection = self.mds.CollectionNode.get(public_key=channel_pk, id_=channel_id) try: request_parsed = await request.json() except (ContentTypeError, ValueError): return RESTResponse({"error": "Bad JSON"}, status=HTTP_BAD_REQUEST) if not target_collection and not personal_root: return RESTResponse({"error": "Target channel not found"}, status=HTTP_NOT_FOUND) results_list = [] for entry in request_parsed: public_key, id_ = unhexlify(entry["public_key"]), entry["id"] source = self.mds.ChannelNode.get(public_key=public_key, id_=id_) if not source: return RESTResponse({"error": "Source entry not found"}, status=HTTP_BAD_REQUEST) # We must upgrade Collections to Channels when moving them to root channel, and, vice-versa, # downgrade Channels to Collections when moving them into existing channels if isinstance(source, self.mds.CollectionNode): src_dict = source.to_dict() if channel_id == 0: rslt = self.mds.ChannelMetadata.create_channel(title=source.title) else: dst_dict = {'origin_id': channel_id, "status": NEW} for k in self.mds.CollectionNode.nonpersonal_attributes: dst_dict[k] = src_dict[k] dst_dict.pop("metadata_type") rslt = self.mds.CollectionNode(**dst_dict) for child in source.actual_contents: child.make_copy(rslt.id_) else: rslt = source.make_copy(channel_id) results_list.append(rslt.to_simple_dict()) return RESTResponse(results_list) @docs( tags=['Metadata'], summary='Create a new channel entry in the given channel.', responses={ 200: { 'description': 'Returns the newly created channel', 'schema': schema(CreateChannelResponse={'results': [ChannelSchema]}), } }, ) async def create_channel(self, request): with db_session: _, channel_id = self.get_channel_from_request(request) request_parsed = await request.json() channel_name = request_parsed.get("name", "New channel") md = self.mds.ChannelMetadata.create_channel(channel_name, origin_id=channel_id) return RESTResponse({"results": [md.to_simple_dict()]}) @docs( tags=['Metadata'], summary='Create a new collection entry in the given channel.', responses={ 200: { 'description': 'Returns the newly created collection', 'schema': schema(CreateCollectionResponse={'results': [ChannelSchema]}), } }, ) async def create_collection(self, request): with db_session: _, channel_id = self.get_channel_from_request(request) request_parsed = await request.json() collection_name = request_parsed.get("name", "New collection") md = self.mds.CollectionNode(origin_id=channel_id, title=collection_name, status=NEW) return RESTResponse({"results": [md.to_simple_dict()]}) @docs( tags=['Metadata'], summary='Add a torrent file to your own channel.', responses={ 200: { 'schema': schema( AddTorrentToChannelResponse={'added': (Integer, 'Number of torrent that were added to the channel')} ) }, HTTP_NOT_FOUND: {'schema': HandledErrorSchema, 'example': {"error": "Unknown channel"}}, HTTP_BAD_REQUEST: {'schema': HandledErrorSchema, 'example': {"error": "unknown uri type"}}, }, ) @json_schema( schema( AddTorrentToChannelRequest={ 'torrent': (String, 'Base64-encoded torrent file'), 'uri': (String, 'Add a torrent from a magnet link or URL'), 'torrents_dir': (String, 'Add all .torrent files from a chosen directory'), 'recursive': (Boolean, 'Toggle recursive scanning of the chosen directory for .torrent files'), 'description': (String, 'Description for the torrent'), } ) ) async def add_torrent_to_channel(self, request): channel_pk, channel_id = self.get_channel_from_request(request) with db_session: channel = self.mds.CollectionNode.get(public_key=channel_pk, id_=channel_id) if not channel: return RESTResponse({"error": "Unknown channel"}, status=HTTP_NOT_FOUND) parameters = await request.json() extra_info = {} if parameters.get('description', None): extra_info = {'description': parameters['description']} # First, check whether we did upload a magnet link or URL if parameters.get('uri', None): uri = parameters['uri'] if uri.startswith("http:") or uri.startswith("https:"): data = await _fetch_uri(uri) tdef = TorrentDef.load_from_memory(data) elif uri.startswith("magnet:"): _, xt, _ = parse_magnetlink(uri) if ( xt and is_infohash(codecs.encode(xt, 'hex')) and (self.mds.torrent_exists_in_personal_channel(xt) or channel.copy_torrent_from_infohash(xt)) ): return RESTResponse({"added": 1}) meta_info = await self.download_manager.get_metainfo(xt, timeout=30, url=uri) if not meta_info: raise RuntimeError("Metainfo timeout") tdef = TorrentDef.load_from_dict(meta_info) else: return RESTResponse({"error": "unknown uri type"}, status=HTTP_BAD_REQUEST) added = 0 if tdef: channel.add_torrent_to_channel(tdef, extra_info) added = 1 return RESTResponse({"added": added}) torrents_dir = None if parameters.get('torrents_dir', None): torrents_dir = parameters['torrents_dir'] if not Path(torrents_dir).is_absolute(): return RESTResponse({"error": "the torrents_dir should point to a directory"}, status=HTTP_BAD_REQUEST) recursive = False if parameters.get('recursive'): recursive = parameters['recursive'] if not torrents_dir: return RESTResponse( {"error": "the torrents_dir parameter should be provided when the recursive parameter is set"}, status=HTTP_BAD_REQUEST, ) if torrents_dir: torrents_list, errors_list = channel.add_torrents_from_dir(torrents_dir, recursive) return RESTResponse({"added": len(torrents_list), "errors": errors_list}) if not parameters.get('torrent', None): return RESTResponse({"error": "torrent parameter missing"}, status=HTTP_BAD_REQUEST) # Try to parse the torrent data # Any errors will be handled by the error_middleware torrent = base64.b64decode(parameters['torrent']) torrent_def = TorrentDef.load_from_memory(torrent) channel.add_torrent_to_channel(torrent_def, extra_info) return RESTResponse({"added": 1}) @docs( tags=['Metadata'], summary='Commit a channel.', responses={200: {'schema': schema(CommitResponse={'success': Boolean()})}}, ) async def post_commit(self, request): channel_pk, channel_id = self.get_channel_from_request(request) with db_session: if channel_id == 0: for t in self.mds.CollectionNode.commit_all_channels(): self.gigachannel_manager.updated_my_channel(TorrentDef.load_from_dict(t)) else: coll = self.mds.CollectionNode.get(public_key=channel_pk, id_=channel_id) if not coll: return RESTResponse({"success": False}, status=HTTP_NOT_FOUND) torrent_dict = coll.commit_channel_torrent() if torrent_dict: self.gigachannel_manager.updated_my_channel(TorrentDef.load_from_dict(torrent_dict)) return RESTResponse({"success": True}) @docs( tags=['Metadata'], summary='Check if a channel has uncommitted changes.', responses={200: {'schema': schema(IsChannelDirtyResponse={'dirty': Boolean()})}}, ) async def is_channel_dirty(self, request): channel_pk, _ = self.get_channel_from_request(request) with db_session: dirty = self.mds.MetadataNode.exists(lambda g: g.public_key == channel_pk and g.status in DIRTY_STATUSES) return RESTResponse({"dirty": dirty}) @docs( tags=['Metadata'], summary='Get the list of most popular torrents. Functions as a pseudo-channel.', responses={ 200: { 'schema': schema( GetPopularTorrentsResponse={ 'results': [TorrentSchema], 'first': Integer(), 'last': Integer(), } ) } }, ) async def get_popular_torrents_channel(self, request): sanitized = self.sanitize_parameters(request.query) sanitized["metadata_type"] = REGULAR_TORRENT sanitized["popular"] = True with db_session: contents = self.mds.get_entries(**sanitized) contents_list = [c.to_simple_dict() for c in contents] self.add_download_progress_to_metadata_list(contents_list) self.add_tags_to_metadata_list(contents_list, hide_xxx=sanitized["hide_xxx"]) response_dict = { "results": contents_list, "first": sanitized['first'], "last": sanitized['last'], } return RESTResponse(response_dict)
class TourBookingUpdateRequestSchema(BaseRequestSchema): status = String(required=False, allow_none=True) image_witness = String(data_key='imageWitness', required=False, allow_none=True)
class Status(Schema): release_codename = ReleaseCodename(required=True) status = String(required=True) description = String(allow_none=True) component = Component(required=False) pocket = Pocket(required=False)
class Schema: silly = String(required=True)
def post(): arg_fields = { 'scheduled_user_id': String(required=True), 'localized_start_time': String(required=True), 'localized_end_time': String(required=True), 'local_tz': String( required=True, validate=validate.OneOf(pytz.all_timezones) ), 'notes': String( required=False, validate=validate.Length(max=512), missing=None, default=None ), 'is_paid': Boolean(default=True, missing=True), 'nonce': String( required=False, default='', missing='', ), 'address_id': String( required=False, ) } args = parser.parse(arg_fields) args['scheduling_user_id'] = g.user_info['user_id'] if args['is_paid']: if args.get('nonce') is None or args.get('nonce') == '': raise EndpointException( 'For paid scheduling, a payment nonce must be supplied.' ) del args['is_paid'] event_info = EventFacade().create_new_event(**args) else: if not current_app.config.get('TESTING'): logging.error( 'Attempted to create unpaid event in prod: {0}'.format( args ) ) raise EndpointException( 'Non-paid events are not allowed.' ) del args['is_paid'] del args['nonce'] event_info = EventDAO().create_new_event( **args ) logging.info( 'Created event for user {0} with user {1} for time range: {2}->{3}' 'at timezone {4}.', args['scheduling_user_id'], args['scheduling_user_id'], args['localized_start_time'], args['localized_end_time'], args['local_tz'] ) return jsonify(EventMarshal().dump(event_info).data)
class ShallowCombinedSchema(BaseSchema): collection_id = String() # Joint entity/document attributes collection = Nested(CollectionSchema()) schema = SchemaName() schemata = List(SchemaName()) names = List(String()) addresses = List(String()) phones = List(String()) emails = List(String()) identifiers = List(String()) countries = List(Country()) dates = List(PartialDate()) bulk = Boolean() # Entity attributes foreign_id = String() name = String() entities = List(String()) properties = Dict() # Document attributes status = String() content_hash = String() uploader_id = String() uploader = Nested(RoleReferenceSchema()) error_message = String() title = String() summary = String() languages = List(Language()) keywords = List(String()) date = PartialDate() authored_at = PartialDate() modified_at = PartialDate() published_at = PartialDate() retrieved_at = PartialDate() file_name = String() file_size = Integer() author = String() generator = String() mime_type = String() extension = String() encoding = String() source_url = String() pdf_version = String() columns = List(String()) headers = Dict() children = Integer() # TODO: is this a separate endpoint? text = String() html = String() def document_links(self, data, pk, schemata): links = { 'self': url_for('documents_api.view', document_id=pk), 'tags': url_for('entities_api.tags', id=pk), 'ui': document_url(pk) } if data.get('content_hash'): links['file'] = url_for('documents_api.file', document_id=pk, _authorize=True) if schemata.intersection([Document.SCHEMA_PDF]): links['pdf'] = url_for('documents_api.pdf', document_id=pk, _authorize=True) if schemata.intersection([Document.SCHEMA_PDF, Document.SCHEMA_TABLE]): links['records'] = url_for('documents_api.records', document_id=pk) if schemata.intersection([Document.SCHEMA_FOLDER]): query = (('filter:parent.id', pk), ) links['children'] = url_for('documents_api.index', _query=query) return links def entity_links(self, data, pk, schemata): return { 'self': url_for('entities_api.view', id=pk), # 'similar': url_for('entities_api.similar', id=pk), # 'documents': url_for('entities_api.documents', id=pk), 'references': url_for('entities_api.references', id=pk), 'tags': url_for('entities_api.tags', id=pk), 'ui': entity_url(pk) } @post_dump def hypermedia(self, data): pk = str(data.get('id')) collection = data.get('collection', {}) collection_id = collection.get('id') collection_id = collection_id or data.get('collection_id') schemata = set(data.get('schemata', [])) if Document.SCHEMA in schemata: data['links'] = self.document_links(data, pk, schemata) else: data['links'] = self.entity_links(data, pk, schemata) if data.get('bulk'): data['writeable'] = False else: data['writeable'] = request.authz.can_write(collection_id) return data
class DocumentParentSchema(Schema): id = String(allow_none=True) foreign_id = String(allow_none=True)
class EntityUpdateSchema(Schema): name = String(allow_none=True) schema = SchemaName(required=True) properties = Dict()
class PlanSchema(Schema): id = Integer(dump_only=True) name = String(validate=Length(1, 64), required=True) created_at = DateTime(dump_only=True) updated_at = DateTime(dump_only=True) deleted_at = DateTime(dump_only=True)
class CvePackage(Schema): name = String(required=True) source = String(required=True) ubuntu = String(required=True) debian = String(required=True) statuses = List(Nested(Status))
def post(self, user_id): arg_fields = { 'individual': Nested({ 'first_name': String(required=True), 'last_name': String(required=False), 'email': String(required=True), 'phone': String(), # TODO(ian): Do we need to assert this is > 18 years old? 'date_of_birth': DateTime(required=True), 'address': Nested( { 'street_address': String(required=True), 'locality': String(required=True), 'region': String(required=True), 'postal_code': String(required=True) }, required=True) }), # NOTE: Business is only required if requesting user is # registering on behalf of a business. 'business': Nested({ 'legal_name': String(required=True), 'dba_name': String(required=True), # TODO(ian): Only required if `legal_name` is added above. 'tax_id': String(required=True), 'address': Nested({ 'street_address': String(required=True), 'locality': String(required=True), 'region': String(required=True), 'postal_code': String(required=True) }) }), 'funding': Nested({ # TODO(ian): Set something here. The name of my company? 'descriptor': String(missing=''), 'account_number': String(required=True), 'routing_number': String(required=True), }), 'tos_accepted': Boolean(required=True, default=False, missing=False, validate=validate.OneOf([True])), 'register_as_business': Boolean( required=True, default=False, missing=False, ) } args = parser.parse(arg_fields) if args['register_as_business'] and args.get('business') is None: raise EndpointException( 'Whenever registering as a business, business information ' 'must be supplied.') SubmerchantFacade().create_submerchant(args, user_id) logging.info('Successfully created submerchant for email {0} on User ' '{1}.'.format(args['individual']['email'], user_id)) return jsonify({'success': True})
class Note(Schema): author = String(required=True) note = String(required=True)
class SchemaWithDict(Schema): dict_field = Dict(values=String())
class CVESchema(Schema): id = String(required=True) published = ParsedDateTime(allow_none=True) description = String(allow_none=True) ubuntu_description = String(allow_none=True) notes = List(Nested(Note)) priority = String(allow_none=True) status = String(allow_none=True) cvss3 = Float(allow_none=True) packages = List(Nested(CvePackage)) references = List(String()) bugs = List(String()) patches = Dict(keys=String(), values=List(String(), required=False), allow_none=True) tags = Dict(keys=String(), values=List(String(), required=False), allow_none=True)
class RoomAttributesSchema(mm.ModelSchema): title = String(attribute='attribute.title') class Meta: model = RoomAttributeAssociation fields = ('value', 'title')
class LoggedInUserPasswordSchema(marshmallow.Schema): loggedin_user_password = String(required=True, validate=user_password_validator)
class QuestionLoaderSchema(Schema): id = Integer() order = Integer() question = String() options = Dict() type = String() subQuestions = Nested("self", many=True) @validates_schema def validate_schema(self, data): if data.get("id") is not None: if data.get("question") is None: raise ValidationError( "you must provide the text for the question to create the question", "question") if data.get("type") is None: raise ValidationError( "you must provide the type of the question", "type") if (data.get("type") is not None and (data["type"] == "matrix" or data["type"] == "ranking") and data.get("subQuestions") is None): raise ValidationError( "you must provide at least one sub question for matrix and ranking type questions", "subQuestions") @post_load def load_question(self, data): session = get_db() if data.get("id") is not None: question = session.query(QuestionModel).filter_by( id=data["id"]).one_or_none() if question is None: raise ValidationError( f"The id {data[id]} for the question you have provided does not exist", "id") else: question = QuestionModel() if data.get("question") is not None: question.question = data["question"] if data.get("type") is not None: type = session.query(QuestionTypeModel).filter_by( type=data['type']).one_or_none() if type is None: raise ValidationError("The type " + data.get("type") + "is not supported") else: question.type = type if data.get("options") is not None: question.options = data["options"] if data.get("subQuestions") is not None: if question.type.type != "matrix" and question.type.type != "ranking": raise ValidationError( "Only matrix and ranking type questions may have sub questions" ) for sub in question.subQuestions: if sub not in data["subQuestions"]: sub.status = "deactive" for sub in data['subQuestions']: if sub['object'] not in question.subQuestions: question.subQuestions.append(sub['object']) elif sub.status == "deactive": sub.status = "active" for sub in data['subQuestions']: if sub.get('order') is not None: question.set_item_order(sub['order'], sub['object']) elif (question.type.type == "matrix" or question.type.type == "ranking"): for sub in question.subQuestions: if sub.status == "active": sub.status == "deactive" question.status = "deactive" # handle the case of matrix and ranking questions # the sub questions of these types need the parent questionKey as a prefix if (question.questionKey is None and question.type.type == "matrix" and question.type.type == "ranking"): for sub in question.subQuestions: if sub.questionKey is None: sub.set_prefix(question.questionKey) deserialized_return = {"object": question} if data.get("order") is not None: deserialized_return['order'] = data['order'] return deserialized_return
def __init__(self, *args, **kwargs): String.__init__(self, *args, **kwargs) # Insert validation into self.validators so that multiple errors can be # stored. self.validators.insert(0, RFCEmailValidator(error=self.error_messages["invalid"]))
class ReservationUserEventSchema(mm.Schema): id = Number() title = String() url = String() start_dt = DateTime() end_dt = DateTime()
class HandledErrorSchema(Schema): error = String(description='Optional field describing any failures that may have occurred', required=True)
class _Schema(Schema): employee_id = String(required=True) name = String(required=True)
class EventSchema(BaseSchema): name = String() template = String() params = ParamTypes()
class _Schema(Schema): expertise_level = String(options=["expert", "intermediate", "basic"])
request.db_session.add(Log(LogEventType.USER_EMAIL_SET, request, log_info)) user.email_address = email_address user.email_address_note = email_address_note return Response("Your email address has been updated") @ic_view_config( route_name="user", request_method="POST", request_param="ic-trigger-name=enable-two-factor", renderer="two_factor_enabled.jinja2", permission="change_settings", ) @use_kwargs({"code": String()}, location="form") def post_enable_two_factor(request: Request, code: str) -> dict: """Enable two-factor authentication for the user.""" user = request.context if not user.is_correct_two_factor_code(code): raise HTTPUnprocessableEntity("Invalid code, please try again.") request.user.two_factor_enabled = True # Generate 10 backup codes (16 lowercase letters each) request.user.two_factor_backup_codes = [ "".join(random.choices(string.ascii_lowercase, k=16)) for _ in range(10) ]