Add the concept of authentication methods that authenticate an account
where using the telegram login widget is one such method. If a login is
done with an authentication method that's not associated with any
account the session ends up with the data from the authentication
method in order to allow registering a new account with the
authentication method.
This has to be stored on the session as otherwise it wouldn't be
possible to implement authentication methods such as OAuth2 that takes
the user to a third-party site and then redirects the browser back.
If a session is rotate in the middle of a server side rendering then
some random portions of requests made on the server side will fail with
a session taken error as the server is not going to update the cookies
of the client during these requests.
To avoid this pitfall extend the expiry time of sessions to be 10
seconds after the session has been rotated. This is accomplished by
introducing a new timestamp on sessions called the rotateAt at time
alongside the expiresAt time. Sessions used after rotateAt that haven't
been rotated get rotated into a new session and the existing session
gets the expiresAt time set to 10 seconds in the future. Sessions that
are past the expiredAt time have no access.
This makes the logic around session expiry simpler, and also makes it
possible to audit when a session got rotated, and to mark sessions as
expired without a chance to rotate to a new session without having to
resort to a finished flag.
After the change to converting users to tombstones instead of removing
them from the database several places would accidentally use deleted
user accounts instead of ignoring them.
If an anonymous session is detected as taken the logic preventing the
session from being accidentally deleted would also prevent the user from
recovering from a taken anonymous session.
When a session expires close any event streams that have been opened
with that session. This prevents an attacker with a leaked session
cookie from opening a stream and receiving updates indefinitely without
being detected.
By sending the session the event stream is opened with when the stream
is established this closure on session expiry also serves as a way for
a user agent to be notified whenever its own access level changes.
In order to minimise the window of opportunity to steal a session,
automatically rotate it onto a new session on a frequent basis. This
makes a session cookie older than the automatic rollover time less
likely to grant access and more likely to be detected.
Should a stolen session cookie get rotated while the attacker is using
it, the user will be notificed that their session has been taken the
next time they open the app if the user re-visits the website before the
session is discarded.
I've settled on a short-eared owl (Asio Flammeus) to serve as the mascot
and icon for Owltide. This is not a settled and final decision, but
rather a branding that's good enough for now.
To create the mascot I sketched some owls that I have saved to
/assets/mascot/owl-sketch.png and then selected one I liked. Going
clockwise from the top right the sketches were based on the reference
photos [1], [2], and [3] found by searching for "owl" on Wikimedia
Commons. I then lightly refined it to work as a small icon.
To avoid confusing terms that only make sense for software the artworks
are licensed under the Creative Commons Attribution-ShareAlike 4.0
License. This has a similar spirit to the AGPL and ensures that should
improvements be made upon them they can be incorporated in the project.
[1]: https://commons.wikimedia.org/wiki/File:Athene_cunicularia_-near_Goiania,_Goias,_Brazil-8_edit.jpg
[2]: https://commons.wikimedia.org/wiki/File:Hibou_des_marais.jpg
[3]: https://commons.wikimedia.org/wiki/File:Uhu-muc.jpg
The name is inspired by the watchful owl perching from the tree tops
with complete overview of all that's going on combined with -tide in
the sense it's used for in words like summertide and eastertide.
I firmly believe in free software.
The application I'm making here have capabilities that I've not seen in
any system. It presents itself as an opportunity to collaborate on a
tool that serves the people rather than corporations. Whose incentives
are to help people rather, not make the most money. And whose terms
ensure that these freedoms and incentives cannot be taken back or
subverted.
I license this software under the AGPL.
Allow a shift to have no role associated with it in order to simplify
conflict resolution around situations like a shift being created while
the role it was assoiated with was deleted. This also allows for shifts
that are freestanding to be created in case having a role doesn't make
sense for it.
Add a save dialog at the bottom of the screen that is present whenever
there are unsaved changes. This dialog provides a diff between the
client and server state so that the user can easily confirm the changes
they are about to make are the correct changes before applying them to
the server.
Make sure that all of the entities generated in the demo schedule are
updated in the past. Otherwise updates for them will end up rejected
for being before the current entity's updateAt property.
This also sets the origin for the schedule to be yesterday to make it
easiser to demo the schedule between friday and tuesday.
Array.sort() sorts by UTF-16 code points even when the items in the
array are numbers. Fix the schedule breaking when events cross
different powers of 10 in Unix time which caused the junctions to no
longer be sorted by the numeric value of their Unix time.
When modifying the set instead of replacing it with a new set the change
detection logic in Vue.js doesn't properly propagate the change, causing
certain computed properties that depend on them to go stale.
Fix by creating a new set here, which will emit a modelValue:update
event which will propagate through the v-model bindings.
Add routes and admin panel elements for creating a database backup,
restoring from a backup, deleting the existing schedule, and replacing
the database with the demo schedule. These server as crude ways to
manage the data stored in the system.
Instead of having to type in exactly the name of events or shifts and
then hope you remembered it right, replace these interactions with the
custom select component that gives a complete list of the available
choices and allows quickly searching for the right one.
Vue's proxy handling interferres with the new Set methods causing them
to throw a TypeError when used on Set objects that are replaced by
proxies. Workaround by dropping the proxy when using these.
See https://github.com/vuejs/core/issues/11398
When editing the slots of events and shifts there are certain situations
where the event or shift a slot should belong to becomes unclear or
difficult to reliably assign. For example when adding a new slot in the
UI it may be desirable to do so before the user has input the event
or shift the slot should belong to.
In these cases, not being able to store the slot into the schedule makes
the UI logic needlessly complicated. Allow slots to be added that do
not have its assiated relation linked up to make editing and handling
such scenarios easier.
The selection of locations, events, roles, shifts and users using the
native <select> element makes for awkward and difficult interactions.
Add an alternative select control that fixes the issues with the poor
handling and navigation of the control when having many options.
The custom select component can handle the selection of either one or
many entity from a ClientMap of entiteis with a name. Typing into the
text box searches the entities by name, arrow keys can navigate and
enter confirms the chosen entity by toggling it's presence in the
selection.
To avoid hydration matches string operations need to do the same thing
on the client and server. Add utilities for performing sorting and fuzzy
matching of strings that uses the account's locale.
Instead of merging overlapping events and shifts when displaying them in
the timetable which causes a very confusing display, add new rows when
events overlap so that each event can be fully displayed without any
overlapping in the table.
Fix userStore attempting to fetch the users list when it's either not
logged in or not allowed to fetch the users, causing the schedule page
to not render.
Rename the base Entity type to ApiEntity, and the base EntityToombstone
to ApiTombstone to better reflect the reality that its only used in the
API interface and that the client and server types uses its own base if
any.
Remove EntityLiving and pull EntityTombstone out of of the base entity
type so that the types based on ApiEntity are always living entities and
if it's possible for it to contain tombstone this will be explicitly
told with the type including a union with ApiTombstone.
Refactor the types of the ClientEntity and ClientMap to better reflect
the types of the entities it stores and converts to/from.
Use a single mutable location, event, slot, etc, for each unique
resource that keeps track of the local editable client copy and
the server copy of the data contained in it.
This makes it much simpler to update these data structures as I can take
advantage of the v-model bindings in Vue.js and work with the system
instead of against it.
Move the logic that converts the EntityClass of a map to a string and
then back into the class to the payload plugin in order to avoid a
circular dependency where the ClientMap needs to know the entity classes
and the entity classes needs to know the ClientMap.
The only place that doesn't know the type of the entities stored in the
client map is the payload reviver, so it makes sense to keep this logic
contained to the payload plugin.
Rename accounts to users to be consistent with the new naming scheme
where account only referes to the logged in user of the session and
implement live updates of users via a user store which listens for
updates from the event stream.
Create a simple Map like class for storing and keeping track of client
entities that are synced from the server and have local editable state.
This will form the basis for storing entities on the client and should
replace the immutable concept used be the ClientSchedule class.
Create a new mutable ClientEntity type and implement ClientUser on top
of it. The mutable concept is intended to replace the immutable concept
used by the ClientSchedule entities as updating immutable types in a
deep interconnected structure is a lot of hassle for little benefit.
The hour headers indicate the time on the left line of the cell, this
makes them confusing to read. Shift the displayed hour to be in the
middle of the left line of the cell so that it is clear which line is
the start of which hour.