This changeset reverts a change we had made previously where we accidentally locked down the ability for service admins to invite other users to their own service. This removes the platform admin user check and reverts it back to the proper permissions check (including adjusting the tests to account for this).
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
This changeset removes webauthn from the Notify.gov admin app. We are not using webauthn at all in our implementation and will be looking at an entirely different authentication system in the near future.
Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
These are more than a list of permissions: each item includes the
label to use when displaying it as an option on a form. Switching
to a name that reflects how the attributes are used will help to
avoid confusion when we rename some of the other attributes in the
same file in later commits.
I've used the term "admin_roles" in the event data to try and show
that these are not the permissions we store in the DB. This is the
name we use for the abstracted form of permissions in the Admin app.
While we could store the DB permissions, that would be a bit more
effort and arguably it's clearer to keep the event data consistent
with the options the user actually saw / chose.
The reason the email address is considered invalid is because it is the
address of the person doing the inviting.
This commit renames the variable to be more specific and avoid confusion
with the email address of the person being invited.
For most events this makes the purpose of each argument clearer at
the point the event is called. It's still worth having a function
for each event type, as this abstracts knowledge of the event label.
Using a schema approach will make adding new events easier.
In the next commit we'll DRY-up the duplication in the tests as well.
Previously we applied this restriction to Platform Admins, on the
assumption that all of them use a security key to log in. Rather
than making that assumption, we can explicitly check their login
method, which also supports rolling out the feature to more users.
This closes a security loophole, where the auth type of a Platform
Admin could be unwittingly changed when they accept an invite, or
by an admin of a service they are a member of.
We have lots of functions for converting various types of data into
strings to be displayed to the user somewhere.
This commit collects all these functions into their own module, rather
than having them cluttering up `app/__init__.py` or buried amongst
various other things that have ended up in `app/utils.py`.
We shouldn’t have a page where someone can look up any other user’s
email address based on their user ID.
We also don’t want a page where a malicious user could send someone an
link which would get them invited to the service.
Restricting the invite to be populated just from users in their own
organisation doesn’t mitigate against this stuff completely, but they
probably have a way of finding out the email address of someone in their
organisation already.
At the moment users must be invited to join a service. But this means:
- users must know that a service already exists
- they need to know who to ask for an invite
If the user doesn’t know these thing then sometimes they just go ahead
and set up a new service. Which means they have to get all the way to
the point of requesting to go live before we tell them that there’s
already a service with a similar name or purpose.
So we should let users:
1. discover what other services exist in their organisation
2. apply to join a service
3. automatically notify the service managers of their interest
4. be invited by a service manager
5. accept the invite
This commit implements step 4. We can just link them to the invite form
in step 3., but we should make it easy for them to send the invite,
without having to copy and paste email addresses.
So this commit let the invite form be pre-populated with an existing
user’s email address.
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.
Since users of broadcast services will always have the view dashboard
permission and never have the API keys permission we can hide these. And
we should re-label the permissions to make sense in the context of
broadcasting.
For services with the broadcast permission this hides:
- the ‘View dashboard’ permission (and defaults it to _checked_) because
all users of broadcast services will need to see the dashboard
- the ‘Manage API keys’ permission (and defaults it to _not checked_)
because we don’t offer an API integration for broadcast services yet
– if we do we won’t want existing users to automatically get the
permission
It relabels:
- the ‘Send’ permission to ‘Prepare and approve’ to match the current,
slightly clunky language on the templates page
- the ‘Manage settings’ label to not refer to ‘usage’ because broadcast
services won’t incur cost
The session key we use is global.
This means if you open the edit page for two different users in two
different tabs the session for the first tab is overwritten with the
session from the second tab. This means the two users are both set to
the same email address, which causes an exception (email addresses are
unique).
This commit fixes that bug by including the user ID in the session ID.
We mostly rely on the API returning a 404 to generate 404s for trying
to get things with non-UUID IDs. This is fine, except our tests often
mock these API calls. So it could look like everything is working fine,
except the thing your passing in might never be a valid UUID, and thus
would 404 in a non-test environment.
So this commit:
1. uses the `uuid` URL converter everywhere there’s something that looks
like an ID in a URL parameter
2. adds a test which automates checking for 1.
Rather than force us to write the decorators in a specific order let’s
just have one decorator call the other. This should make fewer lines of
code, and fewer annoying test failures. It also means that the same way
of raising a `401` (through the `current_app` method) is used
everywhere.
At the moment we mostly have `user_has_permissions` execute first. It
shouldn’t matter, but it feels right for us to check that a user is
logged in before we check their permissions to a service. Otherwise a
malicious user could (maybe) check if a service ID belongs to a real
service, and go on to do something malicious with that information.
This commit adds some extra test code to enforce that the order is
always the same.
N.B. decorators in Python execute from closest to furthest (from the
line on which the function is defined).
We accidentally miss these sometimes. This code adds a test which
inspects the code to automatically check that any function which:
- handles a route
- accepts a service_id
For each function it checks that each of these routes have the
permissions decorator we’d expect.
Most of the introspection/AST code is adapted from here:
https://mvdwoord.github.io/exploration/2017/08/18/ast_explore.html
Organisation team members will be ultimately interested in the detailed
usage of each service, but shouldn't necessarily have access to the
personal data of that services users.
So we should allow these organisation team members to navigate to live
services usage page from the organisation page. They may need to contact
the team so they should also be able to view the team members page.
So they'll then see just usage and team members pages.
If they are actually a team member of the service they're viewing, then
they'll see the full range of options as usual.
This commit implement the above by adding an extra flag to the
`user.has_permissions` decorator which allows certain pages to be marked
as viewable by an organisation user. The default (for all other existing
pages) is that organisation users don’t have permission.
The data flow of other bits of our application looks like this:
```
API (returns JSON)
⬇
API client (returns a built in type, usually `dict`)
⬇
Model (returns an instance, eg of type `Service`)
⬇
View (returns HTML)
```
The user API client was architected weirdly, in that it returned a model
directly, like this:
```
API (returns JSON)
⬇
API client (returns a model, of type `User`, `InvitedUser`, etc)
⬇
View (returns HTML)
```
This mixing of different layers of the application is bad because it
makes it hard to write model code that doesn’t have circular
dependencies. As our application gets more complicated we will be
relying more on models to manage this complexity, so we should make it
easy, not hard to write them.
It also means that most of our mocking was of the User model, not just
the underlying JSON. So it would have been easy to introduce subtle bugs
to the user model, because it wasn’t being comprehensively tested. A lot
of the changed lines of code in this commit mean changing the tests to
mock only the JSON, which means that the model layer gets implicitly
tested.
For those reasons this commit changes the user API client to return
JSON, not an instance of `User` or other models.
This removes the edit_folder_permission checks from the code, enabling
the folder permissions for all services.
This also fixes folder-related tests to set up appropriate user
permissions.
This should only be merged right after alphagov/notifications-api#2428,
when all other permission stories are done.
Platform admin users can access all template folders, so the folder
permissions form always displays everything as checked for them,
which makes it look like the form isn't actually working. We could
do the check based on folder data, but the field still wouldn't
have any effect on permissions. So instead, we hide it completely
for platform admin users.
Submitting the form will remove any folder permissions from the DB
for the platform admin user (which can still be created by changing
permissions on the template folder 'Manage' page), but that's only
relevant if a user stops being a platform admin but keeps their
Notify services.
When a user's email address is updated, we not allowing it to be changed
to a non-government email address. We now allow a non-gov email address
to be changed to another non-gov email address. Government email
addresses still cannot be changed to non-government email addresses.
Also fixes the link in the error message on the ChangeEmailAddress form -
this was being escaped before.
We should audit when a service manager changes a user profile that is not
their own. This can be recorded in our events table, which is currently
only used to record successful logins.
This adds two new types of event, `update_user_email` and
`update_user_mobile_number` which store the
- browser fingerprint
- IP address
- user id of the user being updated
- user id of the service manager making the change
- original email address and new email address (for `update_user_email`
events)
- original mobile number and new mobile number (for
`update_user_mobile_number` events)
User model is the most natural place for a permission check method,
however this means that we need to pass the full user object to
service model methods and TemplateList instead of user_id.
remove `confirm` from `confirm_remove_user_from_service` as there's
only one action now that the initial confirmation prompt takes place
on the edit permissions page
when you hit the delete button, it flashes the delete button and takes
you to the `/service/../user/../delete` url. If you then click the save
button, it would make a POST to the delete URL... and delete the user.
now the page stays on the edit url, but adds a `?delete=yes` query
string. The dangerous flash banner now has an action field which
defines where the browser will make the POST to (which remains at
/delete).
If a new user is being invited for a service which doesn't have edit
folder permissions turned on, we want to send all folders for that
service to api.
Added a folder permissions form to the page to invite users to services.
This only shows if the service has 'edit_folder_permissions' enabled,
and all folder checkboxes are checked by default. This change means that
InviteApiClient.create_invite now sends folder_permissions through to
notifications_api (so invites get created with folder permissions).
Started passing the folder_permissions through to notifications-api when
accepting an invite. This changes UserApiClient.add_user_to_service to
send folder_permissions to notifications_api so that new users get folder
permissions when they are added to the service.
Integrates the folder permissions form with the updated API endpoint
to store changes in the user folders.
Since user folder permissions are returned in the full list of template
folders for the service we need to invalidate the cache key for it each
time we update user permissions.
We're reusing the logic for the `move_to` nested radios field for the
user folder permissions nested checkboxes.
The main difference between the two forms (aside from the different
input type) is that "Move" form contains the root "Templates" as an
option, whereas the folder permissions doesn't.
It turns out that, because of the way NestedFieldMixin.children and
select_nested macro are implemented the easiest way to get the desired
folder permissions behaviour is to add the root folder as a choice with
a `None` value and `NONE_OPTION_VALUE = None` set on the field, which
allows the `child_map` to be constructed but doesn't display the root
folder checkbox itself since it gets overwritten in the final `child_map`.