Commit Graph

51 Commits

Author SHA1 Message Date
Chris Hill-Scott
554a852e2d Don’t return UUID objects from the UUID convertor
Because it means you often have to cast to string in your application
code just to get your tests passing.

The method being monkey patched is originally defined here: b81aa0f18c/src/werkzeug/routing.py (L1272)
2019-11-07 13:46:24 +00:00
Chris Hill-Scott
600e3affc1 Show user names for events without API changes
This commit introduces a slightly hacky way of putting usernames against
events, given that the API only returns user IDs.

It does so without:
- making changes to the API
- making a pages that could potentially fire off dozens of API calls (ie
  one per user)

This comes with the limitation that it can only get names for those team
members who are still in the team. Otherwise it will say ‘Unknown’.

In the future the API should probably return the name and email address
for the user who initiated the event, and whether that user was acting
in a platform admin capacity.
2019-10-23 13:15:41 +01:00
Chris Hill-Scott
6026ce3f8d Refactor model to put add_to… methods on user
An invited user can’t be added to an organisation or service, only a
real user can. So the methods to do this should be on the user model,
and take the details of the invite as arguments.
2019-06-27 15:48:29 +01:00
Rebecca Law
d344bc7006 Fix a bug with inviting existing users to an organisation.
The method to add the user to the organisation was missing the user id. This PR fixes that.
2019-06-27 15:34:23 +01:00
Chris Hill-Scott
3968d5b766 Allow org team members to see team and usage
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.
2019-06-20 15:37:52 +01:00
Chris Hill-Scott
31afd65e71 Refactor permissions checking to use methods
It’s a bit more concise to use these methods, rather than access the
lists directly.

And because it’s easier to read it will make later refactoring less
bothersome.
2019-06-20 14:32:08 +01:00
Chris Hill-Scott
290e111810 Stop making multiple API calls to get_organisation
The count of live services is coming back from the `/organisations`
response. We don’t need to get each organisation individually now.
2019-06-17 16:14:25 +01:00
Chris Hill-Scott
913095fe60 Wrap a model around list of all organisations 2019-06-17 16:00:59 +01:00
Chris Hill-Scott
0aea038d51 Use new fields for getting orgs and services
Uses https://github.com/alphagov/notifications-api/pull/2539 to reduce
the number of API calls we make.
2019-06-17 15:56:59 +01:00
Chris Hill-Scott
cf3e9302a0 Merge pull request #3015 from alphagov/optional-platform-admin
add option to suppress platform admin temporarily
2019-06-17 10:00:45 +01:00
Chris Hill-Scott
faaa812379 Merge pull request #3013 from alphagov/org-client-refactor
Wrap get org methods in class method on model
2019-06-14 16:56:02 +01:00
Leo Hemsted
c724f84c23 change wording of platform admin toggle to positive rather than negative
CHS Approved Wording 👍

also rename suppress_platform_admin -> disable_platform_admin_view in
the backend, as suppress is a kinda weird word.
2019-06-14 15:13:56 +01:00
Leo Hemsted
7b02cb72c6 add option to suppress platform admin temporarily
so that platform admins (us) can view pages as regular users do easily.
Simply adds a flag in the session cookie that overrides the actual
platform admin flag on the user model if set. This way it's safe, since
this only downgrades existing functionality, so if someone managed to
alter it they could only get less permissions, not more.

You can change this value from the user profile page if either:

* you're a platform admin
* the flag is set (to any value) on the cookie.

This slightly weird check means that we don't check the underlying
`user._platform_admin` flag anywhere in the code, even when toggling
the suppression.
2019-06-14 11:59:12 +01:00
Chris Hill-Scott
04f6bd8132 Wrap get org methods in class method on model
This means the service and user models have to import one fewer thing,
and matches what we’re doing with the `from_id` class method.
2019-06-13 14:23:02 +01:00
Chris Hill-Scott
edba0c7f5f Rename confusing property
It’s more about the multiple different types of things they can see, not
just that there are multiple things.
2019-06-13 13:47:29 +01:00
Chris Hill-Scott
451fadb1b3 Hide H1 for grid layout
With the `<h2>`s on this page it’s fairly explicit what the page is
listing, so user doesn’t need the context provided by the `<h1>`.
2019-06-13 13:47:29 +01:00
Chris Hill-Scott
e31570e93d Refactor to use .belongs_to methods
The view code shouldn’t need to know the internals of a service’s data
structure; the idea of having a service model is to abstract this kind
of thing.
2019-06-13 13:47:28 +01:00
Chris Hill-Scott
71dc650db6 Make user model return a service model, not JSON
This makes it:
- nicer, by having access to sensibly named things like
  `Service.trial_mode` instead of `service['restricted']`.
- less likely to write Jinja code like `service.trail_mode`, which would
  fail silently if `service` was a dictionary
2019-06-13 13:47:28 +01:00
Chris Hill-Scott
062f42b769 Rename all_services property on user
For consistency with `.organisations`/`.organisation_ids`.

`.services` returns a list of semi-rich dictionaries for each service.

`.service_ids` returns service IDs only.
2019-06-13 13:47:28 +01:00
Chris Hill-Scott
f774a10e3a Only show live services without an organisation
In reality we shouldn’t have any live services that don’t have an
organisation. But we probably do locally, in preview, etc., and we
shouldn’t lose a way of accessing them.
2019-06-13 13:47:27 +01:00
Chris Hill-Scott
3be1f79cf9 Add count of live services to organisations
This makes it clear that these are something different to the trial
mode services, in that they are a container of multiple things.
2019-06-13 13:42:11 +01:00
Chris Hill-Scott
63ba3a6f30 Put organisations on the user model
As in other places, putting a model layer between the view and the API
client makes the code cleaner and clearer.
2019-06-13 13:42:11 +01:00
Chris Hill-Scott
88e36d6841 Move some methods from the API client to the model
They make more sense being on the model, and it doesn’t make any sense
to duplicate them.
2019-06-13 13:42:10 +01:00
Chris Hill-Scott
1ca0dfacf7 List all trial mode services
At the moment the service list doesn’t disambiguate between live and
trial mode services. This makes it hard to tell which of the things are
important and which aren’t.

The first step towards making this page clearer is to list trial mode
services separately.
2019-06-13 13:42:10 +01:00
Chris Hill-Scott
6130004b0c Fix inviting existing users
The API needs the id of the user, not the id of the invite.

The problem with the tests is that the update mock returned a different
user ID than the user it was being passed. So the tests didn’t catch
this.
2019-06-06 17:24:48 +01:00
Chris Hill-Scott
f34a252e72 Remove defaults from User model
the api always returns exactly:
```
id
name
email_address
auth_type
current_session_id
failed_login_count
logged_in_at
mobile_number
organisations
password_changed_at
permissions
platform_admin
services
state
```

it does this through `models.py::User.serialize` – there is an old
Marshmallow `user_schema` in `schemas.py` but this isn’t used for
dumping return data, only parsing the json in the create user rest
endpoint.

This means we can rely on these keys always being in the dictionary.
2019-06-05 14:55:43 +01:00
Chris Hill-Scott
e43f78a72d Don’t implement separate __getitem__ for invited users
It can inherit now because both `User.__init__` and
`InvitedUser.__init__` have the same method signature.
2019-06-05 14:54:48 +01:00
Chris Hill-Scott
bd9acb1310 Make invited user model inherit from JSONModel
This is more consistent, and less fiddly that always having to call it
with a dictionary expansion.
2019-06-05 14:39:02 +01:00
Chris Hill-Scott
f8fb2d3c8f Make Users model inherit from sequence
This gives the same behaviour as you’d expect inheriting from `list`.
However because `list` is written with a lot of optimisations it’s
unpredictable to inherit from.

Python provides a series of abstract base classes that you can inherit
from instead.

Further reading:
1. https://docs.python.org/3.4/library/collections.abc.html#collections.abc.Sequence
2. https://treyhunner.com/2019/04/why-you-shouldnt-inherit-from-list-and-dict-in-python/
2019-06-05 12:50:43 +01:00
Chris Hill-Scott
8492496c34 Make permissions a setter
This is better because it reuses native constructs of the language,
rather than half reimplementing them ourselves.
2019-06-05 11:13:42 +01:00
Chris Hill-Scott
0be359b678 Use JSONModel for user model
Our other models inherit from `JSONModel`, rather than manually doing
lookups of the JSON in the `__init__` method. This commit changes the
user model to work in the same way.

I had to add a new concept (`DEFAULTS`) to account for some properties
not always being present in the (mocked) JSON. In reality it might be
that the API does always return these values. This should be looked at
in future work, to see which defaults can be safely removed. At least
now they:
- do not mean any changes are needed to the existing tests
- are explicitly separated from the attributes that we do expect to
  always be in the JSON response
2019-06-05 11:13:41 +01:00
Chris Hill-Scott
628e344b36 Make user API client return JSON, not a model
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.
2019-06-05 11:13:41 +01:00
Chris Hill-Scott
1f601cd807 Remove feature from from user model 2019-05-17 14:08:02 +01:00
Alexey Bezhan
cab780b549 Remove edit_folder_permissions service setting (feature flag)
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.
2019-05-17 11:20:16 +01:00
Chris Hill-Scott
1708d17e40 Set org type to NHS if user has NHS email address
We get people signing up for Notify who work for the NHS, but whose
organisation we don’t know about. For example
`name@gloshospitals.nhs.uk` will be someone working for Gloucestershire
Hospitals NHS Foundation Trust, which is not an organisation we have in
the database.

Currently we rely on knowing the specific organisation (NHS as a whole
isn’t an organisation) in order to set the organisation type for any
services they create. This commit adds a special case for anyone with an
NHS email address to set the organisation type to be NHS, even when we
don’t know which specific part of the NHS they work for.

This is the same thing we do on the API side for NHS email and letter
branding:
a4ae5a0a90/app/dao/services_dao.py (L310-L313)
2019-05-08 12:08:54 +01:00
Chris Hill-Scott
b7e9c320f8 Fix permissions check when service ID is a UUID
If you define a route with the service ID as a typed parameter, ie
```
@main.route('/services/<uuid:service_id>/agreement')
```

then `type(service_id)` returns `<class 'uuid.UUID'>`.

This is a problem when the permissions dictionary stores service IDs as
strings, because trying to look up a user’s permissions with the UUID
fails silently (that key isn’t in the dictionary).

This commit makes sure we always cast the service ID to a string before
using it to check permissions.
2019-05-03 10:06:51 +01:00
Chris Hill-Scott
718f440720 Get info about organisations from database table
This is the first step of replacing the `domains.yml` file.

In order to replicate the same functionality we get from the
`domains.yml` file and its associated code this commit adds a
`Organisation` model. This model copies a lot of methods from the
`AgreementInfo` class which wrapped the `domains.yml` file.

It factors out some stuff that would otherwise be duplicated between the
`Organisation` and `Service` model, in such a way that could be reused
for making other models in the future.

This commit doesn’t change other parts of the code to make use of this
new model yet – that will come in subsequent commits.
2019-04-12 14:01:14 +01:00
Katie Smith
19fe587640 Make InvitedUser class always expect folder_permissions to be returned
InvitedUsers now always have folder_permissions (if they can't view any
folders their folder permissions will be `[]`).
2019-04-09 14:37:37 +01:00
Leo Hemsted
249b80762a add count of folders visible per user to the team members page
Shows a count of how many folders that user can see - this doesn't do
anything smart with parent folder stuff, it's just "how many checkboxes
are ticked on the edit page".

* doesn't show if service has no folders
* doesn't show if service hasn't got folder permissions enabled
2019-04-04 17:55:37 +01:00
Alexey Bezhan
2478c6b608 Add a temporary check for service folder permission
Since we're calling `User.has_template_folder_permission` directly
in a few places (notably the `folder_path` template macro), we need
to check that the service has the feature flag enabled first. This is
usually done by the caller, but template macro doesn't have access to
`current_service`. To avoid passing it in each time the macro is called
we're adding a temporary check inside the method itself.

This commit can be reverted completely when we remove the service
feature flag.
2019-04-01 10:50:39 +01:00
Alexey Bezhan
172f6b303f Fix missing New template / folder buttons on Templates root page
User folder permission check should recognize both `None` folder and
folder with a `None` id as template root.
2019-04-01 10:50:39 +01:00
Alexey Bezhan
af2eb0555d Make sure users always have permission to access top-level templates 2019-04-01 10:50:38 +01:00
Alexey Bezhan
e6d7f7ebeb Add a user method to check folder permission
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.
2019-04-01 10:50:38 +01:00
Katie Smith
782bd34394 Use folder_permissions in the InvitedUser model
We were already invitializing InvitedUser with folder_permissions
(defaulting to None), but this removes the default and adds
folder_permissions to the serialize method. Folder permissions should
now always be returned from api, either as an empty list or a list of
UUIDs.
2019-03-21 10:17:05 +00:00
Katie Smith
9fbe64bbd7 Allow InvitedUser to be initialised with folder_permissions
The `folder_permissions` property has no effect and is not used yet, but
it needs to be added before we add a `folder_permissions` column to the
`InvitedUser` model in notifications-api. This is because we initialize
the InvitedUser class with the response from notifications-api.
2019-03-14 13:36:00 +00:00
Chris Hill-Scott
558ae87baa Hide ‘back to …’ link if it’s not your service
This can happen if you click a link for a service you don’t have access
to. We shouldn’t show the back to service link in this case because:
- you shouldn’t be able to find out the service’s name from just knowing
  the link
- if you click the link you only get a `403` anyway
2019-01-15 17:31:55 +00:00
Chris Hill-Scott
206a7806d4 Allow users to navigate folders when copying
We already have a pattern for navigation folders and searching for
templates – let’s use it for the copy page too. And I reckon we can
represent services as folders if the user has multiple services they
could copy a template from.
2019-01-08 12:21:32 +00:00
Chris Hill-Scott
a8b916b57f Refactor gov user check into a decorator
We quite often use it in the same way as `@user_has_permissions`.
2018-12-12 13:42:26 +00:00
Chris Hill-Scott
126db71de6 Refactor government user check onto model 2018-12-12 12:29:08 +00:00
Rebecca Law
dd22fa06a4 Fix the permission check for users without permissions.
A users without permissions should be redirected to choose-templates page when signing in.
2018-11-19 15:26:43 +00:00