This links up the `get_webauthn_credentials_for_user` and
`create_webauthn_credential_for_user` methods of the user api client to
notifications-api.
To send data to the API we need strings to be unicode, so we call
decode('utf-8') on base64 objects.
Co-authored-by: Leo Hemsted <leo.hemsted@digital.cabinet-office.gov.uk>
This passes existing credentials in the server response, to allow
the browser to prevent re-registering the same key for the same
user. Registering the same key multiple times doesn't seem to be
an issue technically; the user has likely got their keys mixed up.
- Chrome says "you don't need to register it again".
- Safari exits with an InvalidStateError.
- Firefox exits with a DOMException.
This adds Yubico's FIDO2 library and two APIs for working with the
"navigator.credentials.create()" function in JavaScript. The GET
API uses the library to generate options for the "create()" function,
and the POST API decodes and verifies the resulting credential. While
the options and response are dict-like, CBOR is necessary to encode
some of the byte-level values, which can't be represented in JSON.
Much of the code here is based on the Yubico library example [1][2].
Implementation notes:
- There are definitely better ways to alert the user about failure, but
window.alert() will do for the time being. Using location.reload() is
also a bit jarring if the page scrolls, but not a major issue.
- Ideally we would use window.fetch() to do AJAX calls, but we don't
have a polyfill for this, and we use $.ajax() elsewhere [3]. We need
to do a few weird tricks [6] to stop jQuery trashing the data.
- The FIDO2 server doesn't serve web requests; it's just a "server" in
the sense of WebAuthn terminology. It lives in its own module, since it
needs to be initialised with the app / config.
- $.ajax returns a promise-like object. Although we've used ".fail()"
elsewhere [3], I couldn't find a stub object that supports it, so I've
gone for ".catch()", and used a Promise stub object in tests.
- WebAuthn only works over HTTPS, but there's an exception for "localhost"
[4]. However, the library is a bit too strict [5], so we have to disable
origin verification to avoid needing HTTPS for dev work.
[1]: c42d9628a4/examples/server/server.py
[2]: c42d9628a4/examples/server/static/register.html
[3]: 91453d3639/app/assets/javascripts/updateContent.js (L33)
[4]: https://stackoverflow.com/questions/55971593/navigator-credentials-is-null-on-local-server
[5]: c42d9628a4/fido2/rpid.py (L69)
[6]: https://stackoverflow.com/questions/12394622/does-jquery-ajax-or-load-allow-for-responsetype-arraybuffer
This adds a new platform admin settings row, leading a page which
shows any existing keys and allows a new one to be registered. Until
the APIs for this are implemented, the user API client just returns
some stubbed data for manual testing.
This also includes a basic JavaScript module to do the main work of
registering a new authenticator, to be implemented in the next commits.
Some more minor notes:
- Setting the headings in the mapping_table is necessary to get the
horizontal rule along the top (to match the design).
- Setting caption to False in the mapping_table is necessary to stop
an extra margin appearing at the top.
Until all the data is updated to always be "all", we have to handle the
case of provider_restriction being set to None or "all" (which mean the
same thing).
The code can be tidied up once the broadcast provider_restriction is never None.
first of a two step process to remove invited user objects from the
session. we're removing them because they're of variable size, and with
a lot of folder permissions they can cause the session to exceed the 4kb
cookie size limit and not save properly.
this commit looks at invited org users only.
in this step, start saving the invited org user's id to the
session alongside the session object. Then, if the invited_org_user_id
is present in the next step of the invite flow, fetch the user object
from the API instead of from the session. If it's not present (due to a
session set by an older instance of the admin app), then just use the
old code to get the entire object out of the session.
For invites where the user is small enough to persist to the cookie,
this will still save both the old and the new way, but will always make
an extra check to the API, I think this minor performance hit is totally
fine. For invites where the user is too big to persist, they'll still
fail for now, and will need to wait until the next PR comes along and
stops saving the large invited user object to the session entirely.
Note, no option at the moment to set the service broadcast account type
as None, or back to without the broadcast permission. This has been done
for speed of development given the chance of us needing this is very
low. We can add it later if we need to.
Update all methods that were previous calling @cache.delete('service-{service-id}-template-None') to instead call _delete_template_cache_for_service
Remove call to get service templates, it's not needed since all template version cache is being deleted.
We think that in some cases alerts will be composed in the moment, and
therefore making people first create a template is:
- not a good use of their time
- adding some conceptual complexity which they don’t need
This commit makes it possible to type some words and have them go
straight into the `content` field in the database.
In the future we might want to progressively enhance the radio buttons
so they show on the same page (like we do with the grey buttons on the
templates page).
On the uploads page we only show jobs which are within a service’s data
retention.
This commit does the same for when we’re listing the jobs for a contact
list. This matches the UI, which says a contact list has been ‘used
`<count_of_jobs>` in the last <data_retention> days’
It’s a bit unintuitive that starting a job from a contact list makes a
copy of the file, which has no relationship to the list it was copied
from. This is more of an implementation detail, rather than something
that comes from people’s mental models of what is going on. Or at least
that’s what I hypothesise.
I think it’s clearer to show jobs that come from contact lists within
the lists that they were created from. By naming the jobs by template
this gives a clearer view of what messages have been sent to the group
over time.
This bit of code is just a safety check to make sure we don’t
inadvertently updating something we shouldn’t (see
0cfe10639a for context).
`rate_limit` is something we allow the admin app to update now (as of
939029a9be).
We were trying to delete the old 'template-{template-id}' keys but
should have been deleting the new keys which have the service id as part
of the key name. This was causing the cache to not be correctly purged
when we did things like update sender names or set defaults. This should
fix it.
As if this is chosen as default, then the template data will be stale as
they do not have the latest reply to address.
We are already doing this for create/update/delete for sms senders and
contact lists for letters, it was just the create we were missing for
email reply to addresses.
with reset password email.
This is so when users reset their password they are still
redirected to pages they were meant to visit.
This change was done specifically so everyone who is meant to see
broadcast tour sees it, but it will improve lives of all users
who wanted to visit a page on Notify but then had to reset
their password in the process.
We can drop use of the old key as we no longer need to read data from
the old key. Either data exists in the new key and we read it from there
or data doesn't exist in the new key and we go to the API to get it and
then set it in redis.
Note, the previous commit is important because it means we aren't at
risk of when this commit is being deployed out, of us getting stale data
from the old key.
We want to change cache keys for templates and broadcasts to include
their service ID. So cache keys should change from
`template-{template_id}-versions` to
`service-{service_id}-template-{template_id}-versions`.
The first step of this which needs to be deployed as a change first is
to delete both keys when updating service templates (even if they key is
not yet set). This means that when we release code in the next PR to
start setting the new key, we won't run into a case where either the old
or the new key can remain set with stale data.
This shows the green banner with a tick when cancelling a user's
invitation to a service or organisation. The accessibility audit noted
that 'When cancelling an invite a new page loads, however, there is no
immediate indication that the invite has been cancelled.'
In order to display the invited user's email address as part of the
flash message, this adds new methods to the api clients for invites to get
a single invite.
sms sender, email reply to, letter contact blocks.
These are all cached within template, under `template.reply_to` - if the
template doesnt have a specific default, then that field stores the
service default. So when service default changes, we need to clear the
template cache so that it is updated to reflect that.
We already use this pattern for deleting the template cache for a bunch
of templates in `template_folder_api_client.move_to_folder`
This should make the pages slightly quicker to load, because Redis will
return the JSON string faster than the API.
The only change that can happen to a broadcast which doesn’t go through
the admin app is a broadcast ending at its scheduled time. So this could
result in a cached broadcast having a status of `broadcasting` when it
had in fact finished. We already account for this here though:
b2b58ec044/app/models/broadcast_message.py (L89-L94)
The API now[1] accepts requests on `…/guest-list` as well as
`…/whitelist`. This commit starts using the former, which means:
- the use of ‘whitelist’ is fully gone from the admin app
- the API can stop using it, at least in URLs
1. As of https://github.com/alphagov/notifications-api/pull/2928