streetsign_server.logic

When the views start becoming overly complex, as much as possible the logic should be split out to sane sizes functions over here in logic.

Feeds and Posts

streetsign_server.logic.feeds_and_posts

logic for feeds_and_posts views, separated out for clarity.

streetsign_server.logic.feeds_and_posts.can_user_write_and_publish(user, post)

returns a tuple, expressing if ‘user’ has permission to write and publish a post.

streetsign_server.logic.feeds_and_posts.clean_date(in_text)

take some input text, and return a datetime, if possible.

streetsign_server.logic.feeds_and_posts.cleanup_orphaned_media_files()

Scan post_images/ and post_videos/ directories for files that have no corresponding Post record in the database, and remove them. Also removes orphaned thumbnails. Returns a dict with counts of removed files and thumbnails.

streetsign_server.logic.feeds_and_posts.delete_post_and_run_callback(post, typemodule)

before a post is actually deleted, check if there is a ‘pre-delete’ callback on this post type, and run that first. This way, for uploaded images (for instance), the file can be deleted as well.

streetsign_server.logic.feeds_and_posts.if_i_cant_write_then_i_quit(post, user)

checks if a post is editable by a user. If it isn’t, for whatever reason, then raise an appropriate ‘PleaseRedirect’ exception. (reasons could be that it’s in a feed we don’t have write access to, or it’s been published, and we don’t have publish permission to that feed, so the post is now ‘locked’ to us.)

streetsign_server.logic.feeds_and_posts.import_source_posts(source)

Import new posts from an ExternalSource. Returns the number of new posts created. The caller is responsible for calling source.save() if needed.

streetsign_server.logic.feeds_and_posts.post_form_intake(post, form, editor)

takes the values from ‘form’, passes the post contents to the editor ‘receive’ function, and adds all the values into the ‘post’ object.

NOTE: this actually modifies the post it is sent!

streetsign_server.logic.feeds_and_posts.try_to_set_feed(post, new_feed_id, user)

Is this user actually allowed to set the feed of this post to what the form is saying they want to? If so, cool. Return that feed.

Connected Clients

streetsign_server.logic.clients

Tracks connected display clients and provides a “force refresh” command channel.

Display screens are anonymous browsers that poll the server for content. They send a lightweight heartbeat (see static/screens/heartbeat.js) which registers their IP address against the screen/alias they are showing. Admins can bump a per-target refresh counter which the heartbeat hands back to the client, causing it to reload.

Presence and refresh state are ephemeral and process-local, which matches the single-process waitress deployment (see run.py). Nothing here is used for authentication.

streetsign_server.logic.clients.client_ip(request)

Real client IP for request.

When StreetSign runs behind trusted reverse proxies, the ProxyFix middleware (wired up in streetsign_server from the TRUSTED_PROXY_HOPS config) has already rewritten request.remote_addr from X-Forwarded-For. Otherwise this is the socket peer. The result is informational only; authentication and rate limiting use the same request.remote_addr, so TRUSTED_PROXY_HOPS must match the real number of proxies for that value to be trustworthy.

streetsign_server.logic.clients.connected_clients()

Return the list of clients seen within SCREEN_CLIENT_TTL seconds.

Stale entries are purged as a side effect. Each item is a plain dict with an ISO formatted last_seen timestamp, ready to be JSON serialised.

streetsign_server.logic.clients.record_heartbeat(screen_id, screen_name, alias, ip, user_agent)

Register a display client as connected and return its refresh state.

Returns a dict suitable for sending straight back to the client.

streetsign_server.logic.clients.request_refresh(target='all')

Bump the refresh counter for target, forcing matching clients to reload on their next heartbeat. Returns the new counter value.

Raises ValueError for an unrecognised target.

streetsign_server.logic.clients.reset()

Clear all presence and refresh state. Used by tests.

URL Safety

streetsign_server.logic.urlsafety

Validation helpers to guard server-side URL fetches against SSRF.

StreetSign fetches some URLs server-side (RSS feeds, externally-hosted images). Without restriction these are SSRF sinks - an admin (or anyone who can ride an admin session via CSRF) could point the server at internal services (e.g. cloud metadata at 169.254.169.254), at localhost, or at local files via file://. These helpers restrict such fetches to http(s) URLs that resolve to public addresses.

exception streetsign_server.logic.urlsafety.UnsafeURL

Raised when a URL is not safe to fetch server-side.

streetsign_server.logic.urlsafety.check_fetch_url(url, resolve=True)

Raise UnsafeURL if url is not safe to fetch from the server.

Checks the scheme is http(s), that there is a hostname, and (when resolve is True) that the hostname does not resolve to a private, loopback, or link-local address. Returns the url unchanged if OK.