Esempio n. 1
0
    def prepare(self):
        if self.local_only and not self.request.remote_ip in config.get(
                "api_trusted_ip_addresses"):
            log.info(
                "api",
                "Rejected %s request from %s, untrusted address." %
                (self.url, self.request.remote_ip),
            )
            raise APIException(
                "rejected", text="You are not coming from a trusted address.")

        if self.allow_cors:
            self.set_header("Access-Control-Allow-Origin", "*")
            self.set_header("Access-Control-Max-Age", "600")
            self.set_header("Access-Control-Allow-Credentials", "false")

        if not isinstance(self.locale, locale.RainwaveLocale):
            self.locale = self.get_browser_locale()

        self.setup_output()

        if "in_order" in self.request.arguments:
            self._output = []
            self._output_array = True
        else:
            self._output = {}

        if not self.sid:
            self.sid = fieldtypes.integer(self.get_cookie("r4_sid", None))
            hostname = self.request.headers.get("Host", None)
            if hostname:
                hostname = str(hostname).split(":")[0]
                if hostname in config.station_hostnames:
                    self.sid = config.station_hostnames[hostname]
            sid_arg = fieldtypes.integer(self.get_argument("sid", None))
            if sid_arg is not None:
                self.sid = sid_arg
            if self.sid is None and self.sid_required:
                raise APIException("missing_station_id", http_code=400)

        self.arg_parse()

        self.sid_check()

        if self.sid:
            self.set_cookie("r4_sid", str(self.sid), expires_days=365)

        if self.phpbb_auth:
            self.do_phpbb_auth()
        else:
            self.rainwave_auth()

        if not self.user and self.auth_required:
            raise APIException("auth_required", http_code=403)
        elif not self.user and not self.auth_required:
            self.user = User(1)
            self.user.ip_address = self.request.remote_ip

        self.user.refresh(self.sid)

        if self.user and config.get("store_prefs"):
            self.user.save_preferences(self.request.remote_ip,
                                       self.get_cookie("r4_prefs", None))

        self.permission_checks()
Esempio n. 2
0
    def prepare(self):
        if self.path_kwargs.get("station"):
            self.sid = config.stream_filename_to_sid.get(self.path_kwargs["station"])
            if not self.sid:
                self.redirect(config.get("base_site_url"))
                return

        super(MainIndex, self).prepare()

        if self.path_kwargs.get("station") is None:
            self.redirect(
                "{}{}/".format(
                    config.get("base_site_url"),
                    config.station_mount_filenames[self.sid],
                )
            )
            return

        if config.get("enforce_ssl") and self.request.protocol != "https":
            self.redirect(
                "{}{}/".format(
                    config.get("base_site_url"),
                    config.station_mount_filenames[self.sid],
                )
            )
            return

        if not config.get("developer_mode") and self.request.host != config.get(
            "hostname"
        ):
            self.redirect(
                "{}{}/".format(
                    config.get("base_site_url"),
                    config.station_mount_filenames[self.sid],
                )
            )
            return

        if not cache.get_station(self.sid, "sched_current"):
            raise APIException(
                "server_just_started",
                "Rainwave is Rebooting, Please Try Again in a Few Minutes",
                http_code=500,
            )

        self.jsfiles = None

        if not self.user:
            self.user = User(1)
        self.user.ip_address = self.request.remote_ip
        self.user.ensure_api_key()

        if (
            self.beta
            or config.get("developer_mode")
        ):
            buildtools.bake_beta_css()
            buildtools.bake_beta_templates()
            self.jsfiles = []
            for root, _subdirs, files in os.walk(
                os.path.join(os.path.dirname(__file__), "../static/%s" % self.js_dir)
            ):
                for f in files:
                    if f.endswith(".js"):
                        self.jsfiles.append(
                            os.path.join(
                                root[root.find("static/%s" % self.js_dir) :], f
                            )
                        )
Esempio n. 3
0
def check_sync_status(sid, offline_ack=False):
	if not cache.get_station(sid, "backend_ok") and not offline_ack:
		raise APIException("station_offline")
	if cache.get_station(sid, "backend_paused") and not offline_ack:
		raise APIException("station_paused")
Esempio n. 4
0
def attach_info_to_request(request, extra_list = None, all_lists = False, live_voting = False):
	# Front-load all non-animated content ahead of the schedule content
	# Since the schedule content is the most animated on R3, setting this content to load
	# first has a good impact on the perceived animation smoothness since table redrawing
	# doesn't have to take place during the first few frames.

	if request.user:
		request.append("user", request.user.to_private_dict())
		if request.user.is_dj():
			attach_dj_info_to_request(request)

	if not request.mobile:
		if all_lists or (extra_list == "all_albums") or (extra_list == "album") or 'all_albums' in request.request.arguments:
			request.append("all_albums", api_requests.playlist.get_all_albums(request.sid, request.user))
		else:
			request.append("album_diff", cache.get_station(request.sid, 'album_diff'))

		if all_lists or (extra_list == "all_artists") or (extra_list == "artist") or 'all_artists' in request.request.arguments:
			request.append("all_artists", api_requests.playlist.get_all_artists(request.sid))

		if all_lists or (extra_list == "all_groups") or (extra_list == "group") or 'all_groups' in request.request.arguments:
			request.append("all_groups", api_requests.playlist.get_all_groups(request.sid))

		if all_lists or (extra_list == "current_listeners") or 'current_listeners' in request.request.arguments or request.get_cookie("r4_active_list") == "current_listeners":
			request.append("current_listeners", cache.get_station(request.sid, "current_listeners"))

		request.append("request_line", cache.get_station(request.sid, "request_line"))

	sched_next = None
	sched_history = None
	sched_current = None
	if request.user and not request.user.is_anonymous():
		request.append("requests", request.user.get_requests(request.sid))
		sched_current = cache.get_station(request.sid, "sched_current")
		if not sched_current:
			raise APIException("server_just_started", "Rainwave is Rebooting, Please Try Again in a Few Minutes", http_code=500)
		if request.user.is_tunedin():
			sched_current.get_song().data['rating_allowed'] = True
		sched_current = sched_current.to_dict(request.user)
		sched_next = []
		sched_next_objects = cache.get_station(request.sid, "sched_next")
		for evt in sched_next_objects:
			sched_next.append(evt.to_dict(request.user))
		if len(sched_next) > 0 and request.user.is_tunedin() and sched_next_objects[0].is_election and len(sched_next_objects[0].songs) > 1:
			sched_next[0]['voting_allowed'] = True
		if request.user.is_tunedin() and request.user.has_perks():
			for i in range(1, len(sched_next)):
				if sched_next_objects[i].is_election and len(sched_next_objects[i].songs) > 1:
					sched_next[i]['voting_allowed'] = True
		sched_history = []
		for evt in cache.get_station(request.sid, "sched_history"):
			sched_history.append(evt.to_dict(request.user, check_rating_acl=True))
	elif request.user:
		sched_current = cache.get_station(request.sid, "sched_current_dict")
		if not sched_current:
			raise APIException("server_just_started", "Rainwave is Rebooting, Please Try Again in a Few Minutes", http_code=500)
		sched_next = cache.get_station(request.sid, "sched_next_dict")
		sched_history = cache.get_station(request.sid, "sched_history_dict")
		if len(sched_next) > 0 and request.user.is_tunedin() and sched_next[0]['type'] == "Election" and len(sched_next[0]['songs']) > 1:
			sched_next[0]['voting_allowed'] = True
	request.append("sched_current", sched_current)
	request.append("sched_next", sched_next)
	request.append("sched_history", sched_history)
	if request.user:
		if not request.user.is_anonymous():
			user_vote_cache = cache.get_user(request.user, "vote_history")
			if user_vote_cache:
				request.append("already_voted", user_vote_cache)
		else:
			if len(sched_next) > 0 and request.user.data['voted_entry'] > 0 and request.user.data['lock_sid'] == request.sid:
				request.append("already_voted", [(sched_next[0]['id'], request.user.data['voted_entry'])])

	request.append("all_stations_info", cache.get("all_stations_info"))

	if live_voting:
		request.append("live_voting", cache.get_station(request.sid, "live_voting"))
Esempio n. 5
0
    def prepare(self):
        if self.local_only and not self.request.remote_ip in config.get(
                "api_trusted_ip_addresses"):
            log.info(
                "api", "Rejected %s request from %s, untrusted address." %
                (self.url, self.request.remote_ip))
            raise APIException(
                "rejected", text="You are not coming from a trusted address.")

        if not isinstance(self.locale, locale.RainwaveLocale):
            self.locale = self.get_browser_locale()

        if not self.return_name:
            self.return_name = self.url[self.url.rfind("/") + 1:] + "_result"
        else:
            self.return_name = self.return_name

        if self.admin_required or self.dj_required:
            self.login_required = True

        if 'in_order' in self.request.arguments:
            self._output = []
            self._output_array = True
        else:
            self._output = {}

        self.sid = fieldtypes.integer(self.get_cookie("r4_sid", None))
        hostname = self.request.headers.get('Host', None)
        if hostname:
            hostname = unicode(hostname).split(":")[0]
            if hostname in config.station_hostnames:
                self.sid = config.station_hostnames[hostname]
        self.sid = fieldtypes.integer(self.get_argument("sid",
                                                        None)) or self.sid
        if self.sid and not self.sid in config.station_ids:
            self.sid = None
        if not self.sid and self.sid_required:
            raise APIException("missing_station_id", http_code=400)

        for field, field_attribs in self.__class__.fields.iteritems():
            type_cast, required = field_attribs
            if required and field not in self.request.arguments:
                raise APIException("missing_argument",
                                   argument=field,
                                   http_code=400)
            elif not required and field not in self.request.arguments:
                self.cleaned_args[field] = None
            else:
                parsed = type_cast(self.get_argument(field), self)
                if parsed == None and required != None:
                    raise APIException(
                        "invalid_argument",
                        argument=field,
                        reason="%s %s" %
                        (field,
                         getattr(fieldtypes, "%s_error" % type_cast.__name__)),
                        http_code=400)
                else:
                    self.cleaned_args[field] = parsed

        if not self.sid and not self.sid_required:
            self.sid = 5
        if not self.sid in config.station_ids:
            raise APIException("invalid_station_id", http_code=400)
        self.set_cookie("r4_sid",
                        str(self.sid),
                        expires_days=365,
                        domain=config.get("cookie_domain"))

        if self.phpbb_auth:
            self.do_phpbb_auth()
        else:
            self.rainwave_auth()

        if not self.user and self.auth_required:
            raise APIException("auth_required", http_code=403)
        elif not self.user and not self.auth_required:
            self.user = User(1)
            self.user.ip_address = self.request.remote_ip

        self.user.refresh(self.sid)

        if self.login_required and (not self.user or self.user.is_anonymous()):
            raise APIException("login_required", http_code=403)
        if self.tunein_required and (not self.user
                                     or not self.user.is_tunedin()):
            raise APIException("tunein_required", http_code=403)
        if self.admin_required and (not self.user or not self.user.is_admin()):
            raise APIException("admin_required", http_code=403)
        if self.dj_required and (not self.user or not self.user.is_dj()):
            raise APIException("dj_required", http_code=403)
        if self.perks_required and (not self.user
                                    or not self.user.has_perks()):
            raise APIException("perks_required", http_code=403)

        if self.unlocked_listener_only and not self.user:
            raise APIException("auth_required", http_code=403)
        elif self.unlocked_listener_only and self.user.data[
                'lock'] and self.user.data['lock_sid'] != self.sid:
            raise APIException(
                "unlocked_only",
                station=config.station_id_friendly[self.user.data['lock_sid']],
                lock_counter=self.user.data['lock_counter'],
                http_code=403)
Esempio n. 6
0
    def set_template_creds(self, *args, **kwargs):
        """
        Endpoint for Setting Credentials on Template with an option sshkey, to
        the RH.

        Single POST:
            {
                "template": "{{template_name}}",
                "user-name": "{{user_name}}",
                "password": "******",
                "ssh-key":"{{ssh_key}}"
            }

        Array POST:
            [{
                "template": "{{template_name}}",
                "user-name": "{{user_name}}",
                "password": "******",
                "ssh-key":"{{ssh_key}}"
            },
            {
                "template": "{{template_name}}",
            }]
        """
        resp = {}
        rh = ResourceHandler.objects.get(id=self.id)
        handler = rh.cast()
        if not handler.can_import_templates_api:
            raise APIException(_('Bad Request: Invalid Resource Handler'),
                code=400,
                details=_('API endpoint is not currently supported for this resource handler.'))

        profile = self.request.get_user_profile()
        if not profile.is_cbadmin:
            raise PermissionDenied(
                _("This action requires 'CB Admin' or 'Super Admin' privileges."))

        combined_requests = self._verify_api_template_creds_json(self.request.data)
        all_reqs = []
        for req_template, req_username, req_password, req_sshkey in combined_requests:
            logger.info(f"Attempting to set credentials on {req_template}")
            template = handler.os_build_attributes.filter(
                template_name=req_template).first().cast()
            logger.info(template)
            logger.info(dir(template))
            if template:
                template.password = req_password
                template.username = req_username
                template.save()
                handler.save()
                message = f"Template Credentials updated for {template}."
                resp['message'] = message
                resp['status_code'] = 200
                all_reqs.append(resp)
            else:
                message = f"Template {req_template} Not Found"
                resp['message'] = message
                resp['status_code'] = 400
                all_reqs.append(resp)
        overall = {}
        overall['message'] = f"Template Credentials updated for {template}."
        overall['status_code'] = 200
        for resp in all_reqs:
            if resp['status_code'] != 200:
                overall['status_code'] = resp['status_code']
                overall['message'] = resp['message']
        return Response(overall, status=overall['status_code'])
Esempio n. 7
0
def delete_deck(
    deck_id: int,
    session: db.Session = Depends(get_session),
    current_user: "******" = Depends(login_required),
):
    """Delete a deck.

    When requested for a source deck:

    * If there are no snapshots, the deck is truly deleted (unrecoverable). Intended use-case
      is for decks that get auto-saved, but not completed.
    * For decks with snapshots, it's a soft deletion that can potentially be recovered from (no
      user-facing support for recovering deleted decks currently planned, though). Deleted decks
      and their snapshots will be removed from all listings (including the stream).

    When requested for a snapshot, it's a soft deletion and the snapshot will no longer show up
    in any listings (including the stream).
    """
    deck: Deck = session.query(Deck).options(db.joinedload("source")).get(deck_id)
    if not deck or deck.user_id != current_user.id:
        raise NoUserAccessException(detail="You cannot delete a deck you do not own.")
    if deck.is_legacy:
        raise APIException(detail="You cannot delete legacy decks.")
    success_response = Response(status_code=status.HTTP_204_NO_CONTENT)
    # If the deck was previously deleted, just claim success and call it good
    if deck.is_deleted:
        return success_response
    # Check if we have any snapshots for source decks, and just delete that sucker for real if not
    if (
        not deck.is_snapshot
        and session.query(Deck).filter(Deck.source_id == deck.id).count() == 0
    ):
        session.query(DeckCard).filter(DeckCard.deck_id == deck.id).delete(
            synchronize_session=False
        )
        session.query(DeckDie).filter(DeckDie.deck_id == deck_id).delete(
            synchronize_session=False
        )
        session.query(DeckSelectedCard).filter(
            DeckSelectedCard.deck_id == deck_id
        ).delete(synchronize_session=False)
        session.query(Deck).filter(Deck.id == deck_id).delete(synchronize_session=False)
        session.commit()
        return success_response

    # Otherwise, we're looking at a snapshot or a source deck that has snapshots
    deck.is_deleted = True
    # For snapshots, we need to remove only the Stream entries for that snapshot (leave the rest
    #  of the source deck's snapshots alone).
    if deck.is_snapshot and deck.is_public:
        # Check to see if we have a Stream entry that needs updating
        stream_entry: Stream = (
            session.query(Stream)
            .filter(
                Stream.source_entity_id == deck.source.entity_id,
                Stream.entity_type == "deck",
                Stream.entity_id == deck.entity_id,
            )
            .first()
        )
        if stream_entry:
            # We have a stream entry pointed to this snapshot, so check if we have an older snapshot
            #  that we can swap in
            previous_snapshot: Deck = (
                session.query(Deck)
                .filter(
                    Deck.source_id == deck.source_id,
                    Deck.created < deck.created,
                    Deck.is_deleted.is_(False),
                )
                .order_by(Deck.created.desc())
                .first()
            )
            if previous_snapshot:
                stream_entry.entity_id = previous_snapshot.entity_id
                stream_entry.posted = previous_snapshot.created
            else:
                # Otherwise, just delete the stream entry because this deck no longer has any public
                #  snapshots
                session.delete(stream_entry)
    elif not deck.is_snapshot:
        # If we're not deleting a snapshot, then we need to completely clear out the Stream entry
        session.query(Stream).filter(
            Stream.source_entity_id == deck.entity_id, Stream.entity_type == "deck"
        ).delete(synchronize_session=False)
        # And mark all snapshots as deleted
        session.query(Deck).filter(
            Deck.source_id == deck.id,
            Deck.is_snapshot.is_(True),
            Deck.is_deleted.is_(False),
        ).update({"is_deleted": True}, synchronize_session=False)
    # Commit any pending changes, and return success
    session.commit()
    return success_response
Esempio n. 8
0
	def _process_message(self, message, is_throttle_process=False):
		message_id = None
		if "message_id" in message:
			message_id = fieldtypes.zero_or_greater_integer(message['message_id'])

		throt_t = timestamp() - 3
		self.msg_times = [ t for t in self.msg_times if t > throt_t ]

		if not is_throttle_process:
			self.msg_times.append(timestamp())
			# log.debug("throttle", "%s - %s" % (len(self.msg_times), message['action']))
			if self.throttled:
				# log.debug("throttle", "Currently throttled, adding to queue.")
				self.throttled_msgs.append(message)
				return
			elif len(self.msg_times) >= 5:
				# log.debug("throttle", "Too many messages, throttling.")
				self.throttled = True
				self.throttled_msgs.append(message)
				tornado.ioloop.IOLoop.instance().add_timeout(datetime.timedelta(seconds=0.5), self.process_throttle)
				return

		if message['action'] == "ping":
			self.write_message({ "pong": { "timestamp": timestamp() } })
			return

		if message['action'] == "pong":
			self.write_message({ "pongConfirm": { "timestamp": timestamp() } })
			return

		if message['action'] == "vote":
			zeromq.publish({ "action": "vote_by", "by": self.votes_by_key })

		if message['action'] == "check_sched_current_id":
			self._do_sched_check(message)
			return

		message['action'] = "/api4/%s" % message['action']
		if not message['action'] in api_endpoints:
			self.write_message({ "wserror": { "tl_key": "websocket_404", "text": self.locale.translate("websocket_404") } })
			return

		endpoint = api_endpoints[message['action']](websocket=True)
		endpoint.locale = self.locale
		endpoint.request = FakeRequestObject(message, self.request.cookies)
		endpoint.sid = message['sid'] if ('sid' in message and message['sid']) else self.sid
		endpoint.user = self.user
		#pylint: disable=W0212
		try:
			startclock = timestamp()
			# TODO: this should be a part of prepare_standalone!
			# it's required to see if another person on the same IP address has overriden the vote
			# for the in-memory user here, so it requires a DB fetch.
			if message['action'] == "/api4/vote" and self.user.is_anonymous():
				self.user.refresh(self.sid)
			if "message_id" in message:
				if message_id == None:
					endpoint.prepare_standalone()
					raise APIException("invalid_argument", argument="message_id", reason=fieldtypes.zero_or_greater_integer_error, http_code=400)
				endpoint.prepare_standalone(message_id)
			else:
				endpoint.prepare_standalone()
			endpoint.post()
			endpoint.append("api_info", { "exectime": timestamp() - startclock, "time": round(timestamp()) })
			if endpoint.sync_across_sessions:
				if endpoint.return_name in endpoint._output and isinstance(endpoint._output[endpoint.return_name], dict) and not endpoint._output[endpoint.return_name]['success']:
					pass
				else:
					zeromq.publish({ "action": "result_sync", "sid": self.sid, "user_id": self.user.id, "data": endpoint._output, "uuid_exclusion": self.uuid })
			if message['action'] == "/api4/vote" and endpoint.return_name in endpoint._output and isinstance(endpoint._output[endpoint.return_name], dict) and endpoint._output[endpoint.return_name]['success']:
				live_voting = rainwave.schedule.update_live_voting(self.sid)
				endpoint.append("live_voting", live_voting)
				if self.should_vote_throttle():
					zeromq.publish({ "action": "delayed_live_voting", "sid": self.sid, "uuid_exclusion": self.uuid, "data": { "live_voting": live_voting } })
				else:
					zeromq.publish({ "action": "live_voting", "sid": self.sid, "uuid_exclusion": self.uuid, "data": { "live_voting": live_voting } })
		except Exception as e:
			endpoint.write_error(500, exc_info=sys.exc_info(), no_finish=True)
			log.exception("websocket", "API Exception during operation.", e)
		finally:
			self.write_message(endpoint._output)
Esempio n. 9
0
def save_deck(
    data: DeckIn,
    session: db.Session = Depends(get_session),
    current_user: "******" = Depends(login_required),
):
    """Create or update a deck in place.

    **This is not a patch!** You must pass the entire deck object in the body every time, and it
    will only be treated as an update if there is an ID included.
    """
    # Verify that the user has access to this deck, if we're saving over an existing deck
    if data.id:
        deck_check: Deck = (
            session.query(
                Deck.user_id, Deck.is_legacy, Deck.is_snapshot, Deck.is_deleted
            )
            .filter(Deck.id == data.id)
            .first()
        )
        if not deck_check or deck_check.user_id != current_user.id:
            raise NoUserAccessException(detail="You cannot save a deck you do not own.")
        if deck_check.is_legacy:
            raise APIException(detail="Legacy decks cannot be saved.")
        if deck_check.is_snapshot:
            raise APIException(detail="You cannot save over a snapshot.")
        if deck_check.is_deleted:
            raise APIException(detail="This deck has been deleted.")
    # Ensure we have a Phoenixborn stub
    phoenixborn_stub = (
        data.phoenixborn
        if isinstance(data.phoenixborn, str)
        else data.phoenixborn.get("stub")
    )
    phoenixborn = (
        session.query(Card.id, Card.name)
        .filter(Card.stub == phoenixborn_stub, Card.is_legacy.is_(False))
        .first()
    )
    if not phoenixborn:
        raise APIException(detail="Valid Phoenixborn is required.")
    try:
        deck = create_or_update_deck(
            session,
            current_user,
            phoenixborn=phoenixborn,
            deck_id=data.id,
            title=data.title,
            description=data.description,
            dice=[x.dict() for x in data.dice] if data.dice else None,
            cards=[x.dict() for x in data.cards] if data.cards else None,
            first_five=data.first_five,
            effect_costs=data.effect_costs,
            tutor_map=data.tutor_map,
        )
    except PhoenixbornInDeck:
        raise APIException(
            detail="Your deck listing includes a Phoenixborn. Please pass the Phoenixborn at the root level of the deck object."
        )
    except ConjurationInDeck as e:
        raise APIException(
            detail=f"Your deck includes the conjuration {e.card_name}, but conjurations should not be included in the list of cards."
        )
    except BadPhoenixbornUnique as e:
        raise APIException(
            detail=f"Your deck includes {e.card_name}, but this card requires {e.required_phoenixborn}."
        )
    return deck_to_dict(session, deck=deck)
Esempio n. 10
0
def create_snapshot(
    deck_id: int,
    data: SnapshotIn = None,
    session: db.Session = Depends(get_session),
    current_user: "******" = Depends(login_required),
):
    """Create a snapshot of the currently-saved version of the deck.

    **Please note:** you must save the deck prior to calling this endpoint! This endpoint will create a snapshot from
    the most recent saved copy of the deck (although it does allow you to set a custom title and description).
    """
    deck: Deck = (
        session.query(Deck)
        .options(
            db.joinedload("cards"),
            db.joinedload("dice"),
            db.joinedload("selected_cards"),
        )
        .get(deck_id)
    )
    if not deck or deck.user_id != current_user.id:
        raise NoUserAccessException(
            detail="You cannot save a snapshot of a deck you do not own."
        )
    if deck.is_legacy:
        raise APIException(detail="You cannot save snapshots for legacy decks.")
    if deck.is_snapshot:
        raise APIException(detail="You cannot create a snapshot of another snapshot.")
    if deck.is_deleted:
        raise APIException(
            detail="This deck has been deleted and can no longer be updated."
        )
    if not data:
        data = SnapshotIn()
    # Ensure that public snapshots are legal decks
    if data.is_public:
        total_cards = 0
        total_dice = 0
        for deck_card in deck.cards:
            total_cards += deck_card.count
        for deck_die in deck.dice:
            total_dice += deck_die.count
        if total_cards != 30 or total_dice != 10:
            raise APIException(
                detail="You must have exactly 30 cards and 10 dice in your deck to publish it."
            )
    # If setting a preconstructed release, ensure that we have permissions and a valid release
    preconstructed_release_id = None
    if data.preconstructed_release:
        if not current_user.is_admin:
            raise NoUserAccessException(
                detail="Only site admins may publish preconstructed decks."
            )
        if not data.is_public:
            raise APIException(
                detail="Only public decks may be associated with a preconstructed deck."
            )
        preconstructed_release_id = (
            session.query(Release.id)
            .outerjoin(Deck, Deck.preconstructed_release == Release.id)
            .filter(
                Release.stub == data.preconstructed_release,
                Release.is_legacy.is_(False),
                Release.is_public.is_(True),
                Deck.id.is_(None),
            )
            .scalar()
        )
        if not preconstructed_release_id:
            raise APIException(
                detail="No such release, or release already has a preconstructed deck."
            )
    # TODO: Ensure that there is at least one thing changed from the most recent snapshot?
    title = data.title if data.title else deck.title
    description = deck.description
    if data.description:
        description = data.description
    elif data.description is not None:
        # Falsey descriptions that aren't None mean they intentionally want a blank description
        description = None
    create_snapshot_for_deck(
        session,
        current_user,
        deck,
        title=title,
        description=description,
        is_public=data.is_public,
        preconstructed_release_id=preconstructed_release_id,
        include_first_five=data.include_first_five,
    )
    return {"detail": "Snapshot successfully created!"}
Esempio n. 11
0
 def post(self):
     if self.user.add_unrated_requests(self.sid):
         self.append_standard("request_unrated_songs_success")
         self.append("requests", self.user.get_requests(refresh=True))
     else:
         raise APIException("request_unrated_failed")
Esempio n. 12
0
import json
import os

import requests

from api.exceptions import APIException, ExternalAPIException

services_url = os.environ.get('SERVICES_URL')
if services_url is None:
    raise APIException('services_url_not_provided')

headers = {'Content-Type': 'application/json'}


def get_weather(latitude, longitude, time, language):
    data = {
        'latitude': latitude,
        'longitude': longitude,
        'time': time,
        'language': language
    }
    data = json.dumps(data)
    res = requests.post(url=services_url + '/api/weather/',
                        data=data,
                        headers=headers)
    if res.status_code == 200:
        return res.json()
    else:
        raise ExternalAPIException(
            api_name='API Services - Weather',
            description='HTTP code: {}\nDetails: {}'.format(
Esempio n. 13
0
        release = Release(name=data.release, stub=release_stub)
        session.add(release)
        session.commit()

    try:
        card = create_card_service(
            session,
            name=data.name,
            card_type=data.card_type,
            placement=data.placement,
            release=release,
            text=data.text,
            cost=data.cost,
            effect_magic_cost=data.effect_magic_cost,
            can_effect_repeat=data.can_effect_repeat,
            dice=data.dice,
            alt_dice=data.alt_dice,
            phoenixborn=data.phoenixborn,
            attack=data.attack,
            battlefield=data.battlefield,
            life=data.life,
            recover=data.recover,
            spellboard=data.spellboard,
            copies=data.copies,
        )
    except MissingConjurations as e:
        raise APIException(detail=str(e))
    except IntegrityError as e:
        raise APIException(detail="Card already exists!")
    return {"detail": "Card successfully created!"}
Esempio n. 14
0
	def prepare(self):
		self._startclock = time.time()
		self.user = None

		if self.local_only and not self.request.remote_ip in config.get("api_trusted_ip_addresses"):
			log.info("api", "Rejected %s request from %s" % (self.url, self.request.remote_ip))
			self.set_status(403)
			self.finish()

		if not isinstance(self.locale, locale.RainwaveLocale):
			self.locale = self.get_browser_locale()

		if not self.return_name:
			self.return_name = self.url[self.url.rfind("/")+1:] + "_result"
		else:
			self.return_name = self.return_name

		if self.admin_required or self.dj_required:
			self.login_required = True

		if 'in_order' in self.request.arguments:
			self._output = []
			self._output_array = True
		else:
			self._output = {}
			self._output_array = False

		self.sid = fieldtypes.integer(self.get_cookie("r4_sid", "1"))
		if "sid" in self.request.arguments:
			self.sid = int(self.get_argument("sid"))
		elif not self.sid:
			for possible_sid in config.station_ids:
				if self.request.host == config.get_station(possible_sid, "host"):
					self.sid = possible_sid
					break
		if not self.sid and self.sid_required:
			raise APIException("missing_station_id", http_code=400)
		if self.sid and not self.sid in config.station_ids:
			raise APIException("invalid_station_id", http_code=400)
		self.set_cookie("r4_sid", str(self.sid), expires_days=365, domain=config.get("cookie_domain"))

		for field, field_attribs in self.__class__.fields.iteritems():
			type_cast, required = field_attribs
			if required and field not in self.request.arguments:
				raise APIException("missing_argument", argument=field, http_code=400)
			elif not required and field not in self.request.arguments:
				self.cleaned_args[field] = None
			else:
				parsed = type_cast(self.get_argument(field), self)
				if parsed == None and required != None:
					raise APIException("invalid_argument", argument=field, reason=getattr(fieldtypes, "%s_error" % type_cast.__name__), http_code=400)
				else:
					self.cleaned_args[field] = parsed

		if self.phpbb_auth:
			self.do_phpbb_auth()
		else:
			self.rainwave_auth()

		if self.auth_required and not self.user:
			raise APIException("auth_required", http_code=403)

		if self.login_required and (not self.user or self.user.is_anonymous()):
			raise APIException("login_required", http_code=403)
		if self.tunein_required and (not self.user or not self.user.is_tunedin()):
			raise APIException("tunein_required", http_code=403)
		if self.admin_required and (not self.user or not self.user.is_admin()):
			raise APIException("admin_required", http_code=403)
		if self.dj_required and (not self.user or not self.user.is_dj()):
			raise APIException("dj_required", http_code=403)
		if self.perks_required and (not self.user or not self.user.has_perks()):
			raise APIException("perks_required", http_code=403)

		if self.unlocked_listener_only and not self.user:
			raise APIException("auth_required", http_code=403)
		elif self.unlocked_listener_only and self.user.data['listener_lock'] and self.user.data['listener_lock_sid'] != self.sid:
			raise APIException("unlocked_only", station=config.station_id_friendly[self.user.data['listener_lock_sid']], lock_counter=self.user.data['listener_lock_counter'], http_code=403)
Esempio n. 15
0
    def vote(self, entry_id, event, lock_count):
        # Subtract a previous vote from the song's total if there was one
        already_voted = False
        if self.user.is_anonymous():
            # log.debug("vote", "Anon already voted: %s" % (self.user.id, self.user.data['voted_entry']))
            if self.user.data['voted_entry'] and self.user.data[
                    'voted_entry'] == entry_id:
                # immediately return and a success will be registered
                return True
            if self.user.data['voted_entry']:
                already_voted = self.user.data['voted_entry']
        else:
            previous_vote = db.c.fetch_row(
                "SELECT entry_id, vote_id, song_id FROM r4_vote_history WHERE user_id = %s AND elec_id = %s",
                (self.user.id, event.id))
            # log.debug("vote", "Already voted: %s" % repr(already_voted))
            if previous_vote and previous_vote['entry_id'] == entry_id:
                # immediately return and a success will be registered
                return True
            elif previous_vote:
                already_voted = previous_vote['entry_id']

        db.c.start_transaction()
        try:
            if already_voted:
                if not event.add_vote_to_entry(already_voted, -1):
                    log.warn(
                        "vote",
                        "Could not subtract vote from entry: listener ID %s voting for entry ID %s."
                        % (self.user.data['listener_id'], already_voted))
                    raise APIException("internal_error")

            # If this is a new vote, we need to check to make sure the listener is not locked.
            if not already_voted and self.user.data[
                    'lock'] and self.user.data['lock_sid'] != self.sid:
                raise APIException(
                    "user_locked", "User locked to %s for %s more songs." %
                    (config.station_id_friendly[self.user.data['lock_sid']],
                     self.user.data['lock_counter']))
            # Issue the listener lock (will extend a lock if necessary)
            if not self.user.lock_to_sid(self.sid, lock_count):
                log.warn(
                    "vote",
                    "Could not lock user: listener ID %s voting for entry ID %s, tried to lock for %s events."
                    % (self.user.data['listener_id'], entry_id, lock_count))
                raise APIException(
                    "internal_error",
                    "Internal server error.  User is now locked to station ID %s."
                    % self.sid)

            if self.user.is_anonymous():
                if not db.c.update(
                        "UPDATE r4_listeners SET listener_voted_entry = %s WHERE listener_id = %s",
                    (entry_id, self.user.data['listener_id'])):
                    log.warn(
                        "vote",
                        "Could not set voted_entry: listener ID %s voting for entry ID %s."
                        % (self.user.data['listener_id'], entry_id))
                    raise APIException("internal_error")
                self.user.update({"listener_voted_entry": entry_id})
            else:
                if already_voted:
                    db.c.update(
                        "UPDATE r4_vote_history SET song_id = %s, entry_id = %s WHERE user_id = %s and entry_id = %s",
                        (event.get_entry(entry_id).id, entry_id, self.user.id,
                         already_voted))
                else:
                    time_window = int(time.time()) - 1209600
                    vote_count = db.c.fetch_var(
                        "SELECT COUNT(vote_id) FROM r4_vote_history WHERE vote_time > %s AND user_id = %s",
                        (time_window, self.user.id))
                    db.c.execute(
                        "SELECT user_id, COUNT(song_id) AS c FROM r4_vote_history WHERE vote_time > %s GROUP BY user_id HAVING COUNT(song_id) > %s",
                        (time_window, vote_count))
                    rank = db.c.rowcount + 1
                    db.c.update(
                        "INSERT INTO r4_vote_history (elec_id, entry_id, user_id, song_id, vote_at_rank, vote_at_count, sid) "
                        "VALUES (%s, %s, %s, %s, %s, %s, %s)",
                        (event.id, entry_id, self.user.id,
                         event.get_entry(entry_id).id, rank, vote_count,
                         event.sid))
                    db.c.update(
                        "UPDATE phpbb_users SET radio_inactive = FALSE, radio_last_active = %s, radio_totalvotes = %s WHERE user_id = %s",
                        (time.time(), vote_count, self.user.id))

                user_vote_cache = cache.get_user(self.user, "vote_history")
                if not user_vote_cache:
                    user_vote_cache = []
                while len(user_vote_cache) > 5:
                    user_vote_cache.pop(0)
                user_vote_cache.append((event.id, entry_id))
                cache.set_user(self.user, "vote_history", user_vote_cache)

            # Register vote
            if not event.add_vote_to_entry(entry_id):
                log.warn(
                    "vote",
                    "Could not add vote to entry: listener ID %s voting for entry ID %s."
                    % (self.user.data['listener_id'], entry_id))
                raise APIException("internal_error")
            db.c.commit()
        except:
            db.c.rollback()
            raise

        return True
Esempio n. 16
0
	def remove_request(self, song_id):
		song_requested = db.c.fetch_var("SELECT reqstor_id FROM r4_request_store WHERE user_id = %s AND song_id = %s", (self.id, song_id))
		if not song_requested:
			raise APIException("song_not_requested")
		return db.c.update("DELETE FROM r4_request_store WHERE user_id = %s AND song_id = %s", (self.id, song_id))
Esempio n. 17
0
 def post(self):
     if self.user.add_request(self.sid, self.get_argument("song_id")):
         self.append_standard("request_success")
         self.append("requests", self.user.get_requests(self.sid))
     else:
         raise APIException("request_failed")
Esempio n. 18
0
    def post(self):
        s = make_searchable_string(self.get_argument("search"))
        if len(s) < 3:
            raise APIException("search_string_too_short")

        s = "%%%s%%" % s

        artists = db.c.fetch_all(
            "SELECT DISTINCT artist_id AS id, artist_name AS name, artist_name_searchable "
            "FROM r4_song_sid "
            "JOIN r4_song_artist USING (song_id) "
            "JOIN r4_artists USING (artist_id) "
            "WHERE sid = %s AND song_exists = TRUE AND artist_name_searchable LIKE %s "
            "ORDER BY artist_name_searchable "
            "LIMIT 50", (self.sid, s))

        if self.user.is_anonymous():
            albums = db.c.fetch_all(
                "SELECT DISTINCT album_id AS id, album_name AS name, album_cool AS cool, CAST(ROUND(CAST(album_rating AS NUMERIC), 1) AS REAL) AS rating, album_name_searchable, "
                "FALSE AS fave, 0 AS rating_user, FALSE as rating_complete "
                "FROM r4_album_sid "
                "JOIN r4_albums USING (album_id) "
                "WHERE sid = %s AND album_exists = TRUE AND album_name_searchable LIKE %s "
                "ORDER BY album_name_searchable "
                "LIMIT 50", (self.sid, s))
        else:
            albums = db.c.fetch_all(
                "SELECT DISTINCT r4_albums.album_id AS id, album_name AS name, album_cool AS cool, CAST(ROUND(CAST(album_rating AS NUMERIC), 1) AS REAL) AS rating, album_name_searchable, "
                "COALESCE(album_fave, FALSE) AS fave, COALESCE(album_rating_user, 0) AS rating_user, COALESCE(album_rating_complete, FALSE) AS rating_complete "
                "FROM r4_album_sid "
                "JOIN r4_albums USING (album_id) "
                "LEFT JOIN r4_album_ratings ON (r4_albums.album_id = r4_album_ratings.album_id AND r4_album_ratings.user_id = %s AND r4_album_ratings.sid = %s) "
                "LEFT JOIN r4_album_faves ON (r4_albums.album_id = r4_album_faves.album_id AND r4_album_faves.user_id = %s) "
                "WHERE r4_album_sid.sid = %s AND album_exists = TRUE AND album_name_searchable LIKE %s "
                "ORDER BY album_name_searchable "
                "LIMIT 50",
                (self.user.id, self.sid, self.user.id, self.sid, s))

        # use bigger query below
        # songs = db.c.fetch_all("SELECT DISTINCT song_id, song_title, album_name, song_origin_sid, song_cool, song_cool_end, song_url, song_length,  FROM r4_song_sid JOIN r4_songs USING (song_id) JOIN r4_albums USING (album_id) WHERE sid = %s AND song_title_searchable LIKE %s", (self.sid, s))

        # base SQL here copy pasted from /rainwave/playlist_objects/album.py
        if self.user.is_anonymous():
            songs = db.c.fetch_all(
                "SELECT r4_song_sid.song_id AS id, song_length AS length, song_origin_sid AS origin_sid, song_title AS title, song_added_on AS added_on, "
                "song_track_number AS track_number, song_disc_number as disc_number, song_title_searchable, "
                "song_url AS url, song_link_text AS link_text, CAST(ROUND(CAST(song_rating AS NUMERIC), 1) AS REAL) AS rating, "
                "FALSE AS requestable, song_cool AS cool, song_cool_end AS cool_end, "
                "song_artist_parseable AS artist_parseable, "
                "0 AS rating_user, FALSE AS fave, "
                "r4_albums.album_name, r4_songs.album_id, r4_albums.album_name_searchable "
                "FROM r4_song_sid "
                "JOIN r4_songs ON (r4_song_sid.song_id = r4_songs.song_id AND r4_songs.song_title_searchable LIKE %s) "
                "JOIN r4_albums ON (r4_songs.album_id = r4_albums.album_id) "
                "WHERE r4_song_sid.song_exists = TRUE AND r4_songs.song_verified = TRUE AND r4_song_sid.sid = %s "
                "ORDER BY album_name_searchable, song_title_searchable "
                "LIMIT 100", (s, self.sid))
        else:
            songs = db.c.fetch_all(
                "SELECT r4_song_sid.song_id AS id, song_length AS length, song_origin_sid AS origin_sid, song_title AS title, song_added_on AS added_on, "
                "song_track_number AS track_number, song_disc_number as disc_number, song_title_searchable, "
                "song_url AS url, song_link_text AS link_text, CAST(ROUND(CAST(song_rating AS NUMERIC), 1) AS REAL) AS rating, "
                "TRUE AS requestable, song_cool AS cool, song_cool_end AS cool_end, "
                "song_artist_parseable AS artist_parseable, "
                "COALESCE(song_rating_user, 0) AS rating_user, COALESCE(song_fave, FALSE) AS fave, "
                "r4_albums.album_name, r4_songs.album_id, r4_albums.album_name_searchable "
                "FROM r4_song_sid "
                "JOIN r4_songs ON (r4_song_sid.song_id = r4_songs.song_id AND r4_songs.song_title_searchable LIKE %s) "
                "LEFT JOIN r4_song_ratings ON (r4_song_sid.song_id = r4_song_ratings.song_id AND user_id = %s) "
                "JOIN r4_albums ON (r4_songs.album_id = r4_albums.album_id) "
                "WHERE r4_song_sid.song_exists = TRUE AND r4_songs.song_verified = TRUE AND r4_song_sid.sid = %s "
                "ORDER BY album_name_searchable, song_title_searchable "
                "LIMIT 100", (s, self.user.id, self.sid))

        self.append("artists", artists)
        self.append("albums", albums)
        self.append("songs", songs)
Esempio n. 19
0
 def post(self):
     if self.user.remove_request(self.get_argument("song_id")):
         self.append_standard("request_deleted")
         self.append("requests", self.user.get_requests(self.sid))
     else:
         raise APIException("request_delete_failed")
Esempio n. 20
0
	def post(self):
		producer = BaseProducer.load_producer_by_id(self.get_argument("sched_id"))
		if not producer:
			raise APIException("internal_error", "Producer ID %s not found." % self.get_argument("sched_id"))
		db.c.update("DELETE FROM r4_schedule WHERE sched_id = %s", (self.get_argument("sched_id"),))
		self.append_standard("success", "Producer deleted.")
Esempio n. 21
0
	def post(self, sid):
		if db.c.fetch_var("SELECT COUNT(*) FROM r4_listeners WHERE listener_ip = '127.0.0.1' AND user_id = 1") == 0:
			db.c.update("INSERT INTO r4_listeners (listener_ip, user_id, sid, listener_icecast_id) VALUES ('127.0.0.1', 1, %s, 1)", (int(sid),))
			self.append_standard("dev_anon_user_tunein_ok", "Anonymous user tune in record completed.")
		else:
			raise APIException(500, "internal_error", "Anonymous user tune in record already exists.")