Commit Graph

3662 Commits

Author SHA1 Message Date
Rebecca Law
38edcae68a Merge pull request #3924 from alphagov/letter-permanent-failure
Permanent failure message for letters
2021-06-15 13:43:26 +01:00
Rebecca Law
d7364eb21a Add permanent-failure to the format_notification_status formatter so the status appears on the activity page. 2021-06-15 13:08:07 +01:00
Ben Thorner
44cf2b16b5 Merge pull request #3923 from alphagov/refactor-email-verify
Split out utils code into separate modules
2021-06-14 10:28:28 +01:00
Chris Hill-Scott
2d36ec8214 Add support for the operator channel
Was just in one of those meetings where it felt like writing this would
take less time than I’d already spent talking about its relative
priority…

---

In the admin app you can already set the broadcast channel as 'test', 'severe' or 'government'.

Aim:
- Add the 'operator' channel to the list of channels you can pick for the admin app broadcast services

Note:
- The API already supports this - https://github.com/alphagov/notifications-api/pull/3262
- The CBC proxy does not yet support the operator channel and this will need a separate card. That card has not yet been written because the interface has not been agreed between us and the MNOs yet.
- Will need to have the ability to select the operator channel for just a single MNO like we do for the other channels
- If we add this, we shouldn't actually start using it until the MNO in question gives us the go ahead.

---

https://www.pivotaltracker.com/story/show/178485177
2021-06-14 08:48:12 +01:00
Katie Smith
cc1a8254df Merge pull request #3928 from alphagov/billing-report
Add total_letters to the billing report
2021-06-11 16:43:29 +01:00
Katie Smith
266f1728e4 Merge pull request #3919 from alphagov/refactor-broadcast-settings
Refactor broadcast settings forms
2021-06-11 16:43:09 +01:00
Katie Smith
7498930bf5 Merge pull request #3925 from alphagov/move-init-to-models
Move broadcast model code into an explicit module
2021-06-11 16:42:54 +01:00
Katie Smith
ca84b37179 Merge pull request #3916 from alphagov/consolidate-test
Revise error tests for setting broadcast types
2021-06-11 16:42:38 +01:00
Katie Smith
c1bfc280b7 Add total_letters to the billing report
This adds an extra column to the report that can be downloaded from
`platform-admin/reports/usage-for-all-services`.
2021-06-11 11:08:33 +01:00
Rebecca Law
aedf875110 Improve permenant failure message.
Update delivery status document page.
2021-06-10 16:34:00 +01:00
Ben Thorner
fba8d09875 Move broadcast model code into an explicit module
Previously this was hidden away in an anonymous __init__.py file.
I did think about splitting the models into individual files, like
we do with the top-level models for the app. Since the models are
only imported in one place - i.e. are all used together - it didn't
seem worth the hassle, so I've kept them in one file.
2021-06-10 15:05:38 +01:00
Chris Hill-Scott
e7713de4a5 Merge pull request #3920 from alphagov/refactor-webauthn-model
Refactor User.webauthn_credentials into a ModelList
2021-06-10 11:07:21 +01:00
Rebecca Law
498092f9ac When a letters has passed our validation but is not the postal provider
is unable to print the letter we need to mark the letter as failed.
If we mark the letter as a technical-failure, we say that we will fix
the issue, which is wrong because we can not fix the issue.
If we mark the letter as validation-failed, the letter is in wrong
bucket so the letter is not viewable/downloadable by the client.

This PR updates the message for a letter marked as permanent-failure to
better reflect what has actually happened.
2021-06-10 08:56:14 +01:00
Ben Thorner
adc49b8792 Add __init__.py file to make pytest happy
Otherwise we get the following error:

    ________________________________________ ERROR collecting tests/app/utils/test_user.py ________________________________________
    import file mismatch:
    imported module 'test_user' has this __file__ attribute:
      /Users/benthorner/Documents/Projects/admin/tests/app/models/test_user.py
    which is not the same as the test file we want to collect:
      /Users/benthorner/Documents/Projects/admin/tests/app/utils/test_user.py
    HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules
2021-06-09 15:56:34 +01:00
Ben Thorner
35301df908 Relocate unit tests for user permission util
Previously these were lumped together with integration-level tests
for specific endpoints, which test the decorator was applied to the
endpoint in question.
2021-06-09 15:38:28 +01:00
Chris Hill-Scott
f6aa5bdfb8 Refactor User.webauthn_credentials into a ModelList
This saves a bit of repetition, and lets us attach other methods to the
collection, rather than having multiple methods on the user object
prefixed with the same name, or random functions floating about.
2021-06-09 15:21:41 +01:00
Ben Thorner
aafb7e9182 Merge separate test for CSV errors function
It's not clear why this was separate from the other utils tests, or
why it was put under main/ - the code under test wasn't in there.
2021-06-09 15:19:01 +01:00
Ben Thorner
0326005aeb Extract template / csv utility code into modules
This follows a similar approach to the previous commits, noting that
one module depends on the other, so we have to extract both together.
2021-06-09 15:19:00 +01:00
Ben Thorner
2a4aa8b4e1 Extract letter utility code into own module
This provides more room for expansion, and reduces the amount of
arbitrary code in the __init__.py file for the new package.
2021-06-09 13:59:06 +01:00
Ben Thorner
7c27646d6a Extract user utility code into own module
This provides more room for expansion, and reduces the amount of
arbitrary code in the __init__.py file for the new package.
2021-06-09 13:19:05 +01:00
Chris Hill-Scott
eca3454a39 Merge pull request #3914 from alphagov/prune-email-domains-list
Prune the email domains list
2021-06-09 10:25:27 +01:00
Chris Hill-Scott
f8f718dff8 Set user to sign in with newly-added key 2021-06-08 09:31:30 +01:00
Ben Thorner
5bfe5f86de Simplify channel selection using radio buttons
This takes a similar approach as in the previous commit. Since the
"training channel" doesn't really exist, we need some extra code
to pre-select it if a service is already in training mode. As in
the previous commit, I've removed a few non-critical test cases
where we really don't need to test exhaustively.

Note that we also need some specific code to avoid pre-selecting an
option for non-broadcast services, which only used to work by fluke:
we would try to populate the field with (False, None, 'all'), which
isn't a valid combination, so nothing was selected.
2021-06-07 17:51:10 +01:00
Ben Thorner
b38cdcad63 Simplify network choice to optional radio buttons
Previously this field had to mimic the final hyphenated string of
the broadcast account type, even though it was only used to select
one of its components. The new, shorter choices make it easier to
simplify the test for the POST request.

I've also deleted a number of test cases for pre-selected radios.
This functionality isn't critical, so we don't need to exhaustively
test every single possible combination of values.
2021-06-07 17:51:09 +01:00
Ben Thorner
ef8cab7fa4 Simplify network choice form to use boolean radio
This follows the same pattern as in other forms [1].

[1]: 1b459d6692/app/templates/views/organisations/add-gp-organisation.html (L20)
2021-06-07 17:51:09 +01:00
Ben Thorner
e848643361 Simplify provider selection with '-all' suffix
Previously we had to cope with two forms of the hyphenated string
we use to represent a pending change in broadcast account type.
Using "all" to mean "all providers" matches the behaviour in the
API [1], and means we can remove some complexity.

"training-test-all" isn't ideal, since the provider is irrelevant
for a training mode service. However, this isn't much worse than
the previous "training-test", noting that the channel also has no
relevance. We'll iterate this in later commits.

[1]: 8e1a144f87/migrations/versions/0352_broadcast_provider_types.py (L14)
2021-06-07 17:51:01 +01:00
Ben Thorner
e3cc16c936 Remove redundant '_get' and '_post' in test names
This is inconsistent with all the other tests in the same file, and
one of them was incorrect ('_post' was testing a GET). I don't think
we get any value from them, given the inconsistency.
2021-06-07 17:33:55 +01:00
Ben Thorner
9f3cd7332e Add missing test for dodgy broadcast account types
This moves the redundant assertions for the service not changing to
where they're actually relevant, by comparing with the happy path [1].

[1]: c5196fbf07/tests/app/main/views/test_service_settings.py (L5858)
2021-06-07 12:58:52 +01:00
Ben Thorner
2a09429e1d Remove duplication between no-radio-selected tests
Previously the network selection case was tested here and also by
'test_post_service_set_broadcast_network_makes_you_choose'.

I've renamed the test to be consistent and more specific.
2021-06-07 12:51:43 +01:00
Leo Hemsted
0993792137 rename verify to complete in api endpoint
it was changed in this PR: https://github.com/alphagov/notifications-api/pull/3260
2021-06-04 12:52:40 +01:00
Leo Hemsted
26ad20719f send people to /two-factor-sms instead of /two-factor
both routes are already valid, however, the link from sign-in sends to
the old link. it fetches whichever URL is second in the route decorator
list when you call `url_for`. Swapping the order around keeps the routes
valid but starts pointing users to the new url.
2021-06-04 12:52:40 +01:00
Leo Hemsted
bb7343d846 pass nextUrl through yubikey flow
the next url comes from sign in via a query param, and needs to go to
the POST /webauthn/authenticate endpoint. That endpoint logs the user
in and returns the redirect to the browser, and will take the next from
the request query params to get there.

also moving the window mocks to beforeEach/afterEach ensures that
promise callbacks from previous tests aren't still associated in future
tests to ensure good test isolation.

unfortunately i couldn't get mocking location for a single js test to
work, but by changing the global config i was able to add some query
params that i can expect to be passed through. Don't love this at all
but not quite sure of a good way round this. I think we're not
practicing very good hygiene and best practices with our mocking and
it's really confounding me here.
2021-06-04 12:52:40 +01:00
Chris Hill-Scott
5c158891aa Prune the email domains list
We only need domains in here which either:
- don’t belong to a single organisation (eg gov.uk)

All other domains should be stored in the database.

This PR removes domains which are now in the database.

Before
---

```sql
select domain from domain where domain in ('gov.uk', 'mod.uk', 'mil.uk', 'd
 dc-mod.org', 'gov.scot', 'parliament.scot', 'parliament.uk', 'nhs.uk', 'nhs.net', 'nhs.scot', 'police.uk', 'scotent.c
 o.uk', 'assembly.wales', 'cjsm.net', 'gov.wales', 'ac.uk', 'sch.uk', 'onevoicewales.wales', 'mtvh.co.uk', 'wmca.org.u
 k', 'suttonmail.org');
 ```

+-----------------+
| domain          |
|-----------------+
| mtvh.co.uk      |
| wmca.org.uk     |
| gov.wales       |
| gov.scot        |
| parliament.uk   |
| assembly.wales  |
| mil.uk          |
| mod.uk          |
| ddc-mod.org     |
| parliament.scot |
| scotent.co.uk   |
+-----------------+

After
---

```sql
select domain from domain where domain in ('gov.uk', 'nhs.uk', 'nhs.ne
 t', 'nhs.scot', 'police.uk', 'cjsm.net', 'ac.uk', 'sch.uk', 'onevoicewales.wales', 'suttonmail.org') ;
```

+----------+
| domain   |
|----------|
+----------+
2021-06-04 11:45:48 +01:00
Chris Hill-Scott
5ea82b0cdc Merge pull request #3911 from alphagov/fix-html-on-old-job-page
Fix HTML showing on old job page
2021-06-03 14:14:55 +01:00
Chris Hill-Scott
a149c6a853 Fix HTML showing on old job page
Using the `Markup` class tells Jinja that the content is safe to render
without any escaping.
2021-06-03 14:01:20 +01:00
Chris Hill-Scott
2a62d6dfb8 Add a success message when security key registered
This makes it clear that there’s nothing more the user needs to do,
until the next time they sign in.
2021-06-03 13:59:43 +01:00
David McDonald
0fcb7778ac Merge pull request #3893 from alphagov/allow-provider-all-channels
Allow setting provider for any channel
2021-06-03 09:36:43 +01:00
David McDonald
d04602c3aa Fix incorrect test having channel as 'all'
'all' isn't a valid channel. It should be one of government, severe or
test. I think this is a mistake and therefore this commit changes it to
what it should be
2021-06-02 18:17:54 +01:00
David McDonald
2d40208fec Merge pull request #3894 from alphagov/webauthn-login-python-tests
Webauthn login
2021-06-02 15:30:36 +01:00
Leo Hemsted
73a444b33a rename webauthn auth functions
_complete_webauthn_authentication -> _verify_webauthn_authentication

This function just does verification of the actual auth process -
checking the challenge is correct, the signature matches the public key
we have stored in our database, etc.

verify_webauthn_login -> _complete_webauthn_login_attempt

This function doesn't do any actual verification, we've already verified
the user is who they say they are (or not), it's about marking the
attempt, either unsuccessful (we bump the failed_login_count in the db)
or successful (we set the logged_in_at and current_session_id in the
db).

This change also informs changes to the names of methods on the user
model and in user_api_client.
2021-06-02 12:06:10 +01:00
Leo Hemsted
e864100be7 make sure error message flashes work properly
flashes are consumed by the jinja template calling get_flashed_messages
in flash_messages.html.

When you call `abort(403)` the 403 error page is rendered, with the
flashed message on it. However, the webauthn endpoints just return that
page to the ajax `fetch`, which ignores the response and just reloads
the page.

Instead of calling abort, we can just return an empty response body and
the 403 error code, so that the flashed messages stay in the session and
will be rendered when the `GET /two-factor-webauthn` request happens
after the js reloads the page.
2021-06-02 12:06:09 +01:00
Leo Hemsted
a3870af87d allow password reset with webauthn login flow 2021-06-02 12:06:09 +01:00
Leo Hemsted
d05f127e41 return 200 to js instead of 302 when logging in
the js fetch function is really not designed to work with 302s. when it
receives a 302, it automatically follows it and fetches the next page.
This is awkward because I don't want js to do all this in ajax, I want
the browser to get the new URL so it can load the page.

A better approach is to view the admin endpoint as a more pure API: the
js sends a request for authentication to the admin app, and the admin
app responds with a 200 indicating success, and then a payload of
relevant data with that.

The relevant data in this case is "Which URL should I redirect to", it
might be the user's list of services page, or it might be a page telling
them that their email needs revalidating.
2021-06-02 11:51:12 +01:00
Leo Hemsted
c29f87f55d increment failed login count on unsuccesful webauthn login
this doesn't include timeouts or other errors on the browser side - the
main thing this catches is if the token doesn't belong to the user.
However I'm not entirely clear if that's something that will be caught
at this point, or if the browser would reject that key as it's not in
the credentials passed in to the begin_authentication process.
2021-06-02 11:51:11 +01:00
Leo Hemsted
92f78b14fe redirect on login; flash errors on failure
the js `fetch` function will follow redirects blindly and return you the
final 200 response. when there's an error, we don't want to go anywhere,
and we want to use the flask `flash` functionality to pop up an error
page (the likely reason for seeing this is using a yubikey that isn't
associated with your user). using `flash` and then
`window.location.reload()` handles this fine.

However, when the user does log in succesfully we need to properly log
them in - this includes:

* checking their account isn't over the max login count
* resetting failed login count to 0 if not
* setting a new session id in the database (so other browser windows are
  logged out)
* checking if they need to revalidate their email access (every 90 days)
* clearing old user out of the cache

This code all happens in the ajax function rather than being in a
separate redirect, so that you can't just navigate to the login flow. I
wasn't able to unit test that function due how it uses the session and
other flask globals, so moved the auth into its own function so it's
easy to stub out all that CBOR nonsense.

TODO: We still need to pass any `next` URLs through the chain from login
page all the way through the javascript AJAX calls and redirects to the
log_in_user function
2021-06-02 11:51:10 +01:00
Pea Tyczynska
ac757b0fc1 Merge pull request #3904 from alphagov/platform-admin-reply-to
Let platform admins add or update service reply to email address without the need for verification.
2021-06-02 10:46:05 +01:00
Katie Smith
d9fd37a485 add test for succesfully logging in with security key
this is a bit complex, but essentially we're using the test variables
defined in the duolabs py_webauthn library [1]. We're already using
their test variables in tests/app/models/test_webauthn_credential.py and
in the webauthn_credential fixture in conftest.py. By using sample
signature, authenticatordata and clientdatajson from the same key we can
test that the library correctly verifies the signed challenge matches
the original.

We needed to transform some of this data as the yubico/fido2 library we
use has a slightly different way of formatting the fields for the
request body, which is why we're doing things like base64 decoding and
converting from hex to bytes in the post data.

The pytest fixture has changed - before it was incomplete/corrupted and
would error when trying to verify the signature. We took the
credential_data from the pytest fixture, converted it to an
AttestedCredentialData using WebauthnCredential.to_credential_data,
modified the public_key private dictionary to add `public_key[-1]: 1`,
and then called `AttestedCredentialData.create` to re-CBOR-encode the
blob.

The `-1: 1` is the numeric ID of the "SECP256R1" elliptic curve
algorithm. The py_webauthn library forces this particular algorithm,
which differs from the sample creds we took from the fido2 lib tests,
which is why we've had to update our data.

[1] https://github.com/duo-labs/py_webauthn/blob/master/tests/test_webauthn.py#L13-L32
2021-06-01 19:22:54 +01:00
Katie Smith
28ee2a1f9a Add tests for GET webauthn_begin_authentication 2021-06-01 19:08:58 +01:00
Leo Hemsted
c26a596839 allow sign in via webauthn credentials
The flow of the code is roughly as follows:

  user clicks button on webauthn page
  js sends GET request
  python reads GET request, sets up login challenge
  python returns login challenge in response
  js reads GET response, passes login challenge to browser
  browser asks user to touch yubikey
  browser returns yubikey challenge response data to js
  js sends POST request with yubikey challenge response data
  python reads yubikey challenge and compares with users creds from db
  if its a match, python signs user in

The login challenge is a PublicKeyCredentialRequestOptions: [1]
The browser function we call is navigator.credentials.get(): [2]
The response to the challenge from the browser is a PublicKeyCredential: [3]

The python server does all the work setting those up and tearing them
back down again (and checking them against the values we have stored in
the database), but we need to do work to convert them to-and-from CBOR.

[1] https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredentialRequestOptions
[2] https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/get
[3] https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential
2021-06-01 19:08:57 +01:00
Leo Hemsted
c203f624ca rename two_factor to two_factor_sms
it's a bit confusing now that there are three endpoints. the other two
are already renamed two_factor_email and two_factor_webauthn
2021-06-01 19:08:57 +01:00