Skip to content

Commit

Permalink
Spaces Phase 1 (elastic#21408)
Browse files Browse the repository at this point in the history
This is generally ready for review. We are awaiting elastic/elasticsearch#32777 to improve handling when users do not have any access to Kibana, but this should not hold up the overall review for this PR.

This PR is massive, there's no denying that. Here's what to focus on:
1) `x-pack/plugins/spaces`: This is, well, the Spaces plugin. Everything in here is brand new. The server code is arguably more important, but feel free to review whatever you see fit.
2) `x-pack/plugins/security`: There are large and significant changes here to allow Spaces to be securable. To save a bit of time, you are free to ignore changes in `x-pack/plugins/security/public`: These are the UI changes for the role management screen, which were previously reviewed by both us and the design team.
3) `x-pack/test/saved_object_api_integration` and `x-pack/test/spaces_api_integration`: These are the API test suites which verify functionality for:
     a) Both security and spaces enabled
     b) Only security enabled
     c) Only spaces enabled

What to ignore:
1) As mentioned above, you are free to ignore changes in `x-pack/plugins/security/public`
2) Changes to `kibana/src/server/*`: These changes are part of a [different PR that we're targeting against master](elastic#23378) for easier review.

A bulk of the changes to the saved objects service are in the namespaces PR, but we have a couple of important changes included here.

We have implemented a priority queue which allows plugins to specify the order in which their SOC wrapper should be applied: `kibana/src/server/saved_objects/service/lib/priority_collection.ts`. We are leveraging this to ensure that both the security SOC wrapper and the spaces SOC wrapper are applied in the correct order (more details below).

This wrapper is very simple, and it is only responsible for two things:
1) Prevent users from interacting with any `space` objects (use the Spaces client instead, described below)
2) Provide a `namespace` to the underlying Saved Objects Client, and ensure that no other wrappers/callers have provided a namespace. In order to accomplish this, the Spaces wrapper uses the priority queue to ensure that it is the last wrapper invoked before calling the underlying client.

This wrapper is responsible for performing authorization checks. It uses the priority queue to ensure that it is the first wrapper invoked. To say another way, if the authorization checks fail, then no other wrappers will be called, and the base client will not be called either. This wrapper authorizes users in one of two ways: RBAC or Legacy. More details on this are below.

`GET /s/marketing/api/saved_objects/index-pattern/foo`

**When both Security and Spaces are enabled:**
1) Saved objects API retrieves an instance of the SOC via `savedObjects.getScopedClient()`, and invokes its `get` function
2) The Security wrapper is invoked.
    a) Authorization checks are performed to ensure user can access this particular saved object at this space.
3) The Spaces wrapper is invoked.
   a) Spaces applies a `namespace` to be used by the underlying client
4) The underlying client/repository are invoked to retrieve the object from ES.

**When only Spaces are enabled:**
1) Saved objects API retrieves an instance of the SOC via `savedObjects.getScopedClient()`, and invokes its `get` function
2) The Spaces wrapper is invoked.
   a) Spaces applies a `namespace` to be used by the underlying client
3) The underlying client/repository are invoked to retrieve the object from ES.

**When only Security is enabled:**
(assume `/s/marketing` is no longer part of the request)
1) Saved objects API retrieves an instance of the SOC via `savedObjects.getScopedClient()`, and invokes its `get` function
2) The Security wrapper is invoked.
   a) Authorization checks are performed to ensure user can access this particular saved object globally.
3) The underlying client/repository are invoked to retrieve the object from ES.

Authorization changes for this project are centered around Saved Objects, and builds on the work introduced in RBAC Phase 1.

When security is enabled, but spaces is disabled, then the authorization model behaves the same way as before: If the user is taking advantage of Kibana Privileges, then we check their privileges "globally" before proceeding. A "global" privilege check specifies `resources: ['*']` when calling the [ES _has_privileges api.](https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-has-privileges.html). Legacy users (non-rbac) will continue to use the underlying index privileges for authorization.

When both plugins are enabled, then the authorization model becomes more fine-tuned. Rather than checking privileges globally, the privileges are checked against a specific resource that matches the user's active space. In order to accomplish this, the Security plugin needs to know if Spaces is enabled, and if so, it needs to ask Spaces for the user's active space. The subsequent call to the `ES _has_privileges api` would use `resources: ['space:marketing']` to verify that the user is authorized at the `marketing` space. Legacy users (non-rbac) will continue to use the underlying index privileges for authorization. **NOTE** The legacy behavior implies that those users will have access to all spaces. The read/write restrictions are still enforced, but there is no way to restrict access to a specific space for legacy auth users.

No authorization performed. Everyone can access everything.

Spaces, when enabled, prevents saved objects of type `space` from being CRUD'd via the Saved Objects Client. Instead, the only "approved" way to work with these objects is through the new Spaces client (`kibana/x-pack/plugins/spaces/lib/spaces_client.ts`).

When security is enabled, the Spaces client performs its own set of authorization checks before allowing the request to proceed. The Spaces client knows which authorization checks need to happen for a particular request, but it doesn't know _how_ to check privileges. To accomplish this, the spaces client will delegate the check security's authorization service.

That's a great question! We did this primarily to simplify the authorization model (at least for our initial release). Accessing regular saved objects follows a predictible authorization pattern (described above). Spaces themselves inform the authorization model, and this interplay would have greatly increased the complexity. We are brainstorming ideas to obselete the Spaces client in favor of using the Saved Objects Client everywhere, but that's certainly out of scope for this release.

A bulk of the changes to enable spaces are centered around saved objects, so we have spent a majority of our time automating tests against the saved objects api.

**`x-pack/test/saved_object_api_integration/`** contains the test suites for the saved objects api. There is a `common/suites` subfolder which contains a bulk of the test logic. The suites defined here are used in the following test configurations:
1) Spaces only: `./spaces_only`
2) Security and spaces: `./security_and_spaces`
3) Security only: `./security_only`

Each of these test configurations will start up ES/Kibana with the appropriate license and plugin set. Each set runs through the entire test suite described in `common/suites`. Each test with in each suite is run multiple times with different inputs, to test the various permutations of authentication, authorization type (legacy vs RBAC), space-level privileges, and the user's active space.

Spaces provides an experimental public API.

**`x-pack/test/spaces_api_integration`** contains the test suites for the Spaces API. Similar to the Saved Objects API tests described above, there is a `common/suites` folder which contains a bulk of the test logic. The suites defined here are used in the following test configurations:
1) Spaces only: `./spaces_only`
2) Security and spaces: `./security_and_spaces`

We did not provide any new functional UI tests for role management, but the existing suite was updated to accomidate the screen rewrite.

We do have a decent suite of jest unit tests for the various components that make up the new role management screen. They're nested within `kibana/x-pack/plugins/security/public/views/management/edit_role`

We did not provide any new functional UI tests for spaces management, but the components that make up the screens are well-tested, and can be found within `kibana/x-pack/plugins/spaces/public/views/management/edit_space`

There are a couple of UI tests that verify _basic_ functionality. They assert that a user can login, select a space, and then choose a different space once inside: `kibana/x-pack/test/functional/apps/spaces`

Notable child PRs are listed below for easier digesting. Note that some of these PRs are built on other PRs, so the deltas in the links below may be outdated. Cross reference with this PR when in doubt.

- Reactify Role Management Screen: elastic#19035
- Space Aware Privileges UI: elastic#21049
- Space Selector (in Kibana Nav): elastic#19497
- Recently viewed Widget: elastic#22492
- Support Space rename/delete: elastic#22586

- ~~Space Aware Saved Objects: elastic#18862
- ~~Add Space ID to document id: elastic#21372
- Saved object namespaces (supercedes elastic#18862 and elastic#21372):  elastic#22357
- Securing saved objects: elastic#21995
- Dedicated Spaces client (w/ security): elastic#21995

- Public Spaces API (experimental): elastic#22501
- Telemetry: elastic#20581
- Reporting: elastic#21457
- Spencer's original Spaces work: elastic#18664
- Expose `spaceId` to "Add Data" tutorials: elastic#22760

Closes elastic#18948

"Release Note: Create spaces within Kibana to organize dashboards, visualizations, and other saved objects. Secure access to each space when X-Pack Security is enabled"
  • Loading branch information
legrego committed Oct 1, 2018
1 parent dfe08d1 commit 748e711
Show file tree
Hide file tree
Showing 479 changed files with 31,555 additions and 4,994 deletions.
3 changes: 2 additions & 1 deletion docs/api.asciidoc
Expand Up @@ -26,14 +26,15 @@ entirely.

[float]
== APIs

* <<spaces-api>>
* <<role-management-api>>
* <<saved-objects-api>>
* <<dashboard-import-api>>
* <<logstash-configuration-management-api>>
* <<url-shortening-api>>
--

include::api/spaces-management.asciidoc[]
include::api/role-management.asciidoc[]
include::api/saved-objects.asciidoc[]
include::api/dashboard-import.asciidoc[]
Expand Down
42 changes: 38 additions & 4 deletions docs/api/role-management/put.asciidoc
Expand Up @@ -30,7 +30,7 @@ that begin with `_` are reserved for system usage.
`elasticsearch`:: (object) Optional {es} cluster and index privileges, valid keys are
`cluster`, `indices` and `run_as`. For more information, see {xpack-ref}/defining-roles.html[Defining Roles].

`kibana`:: (list) A list of objects that specify the <<kibana-privileges>>.
`kibana`:: (object) An object that specifies the <<kibana-privileges>>. Valid keys are `global` and `space`. Privileges defined in the `global` key will apply to all spaces within Kibana, and will take precedent over any privileges defined in the `space` key. For example, specifying `global: ["all"]` will grant full access to all spaces within Kibana, even if the role indicates that a specific space should only have `read` privileges.

===== Example

Expand All @@ -52,13 +52,47 @@ PUT /api/security/role/my_kibana_role
"query" : "{\"match\": {\"title\": \"foo\"}}"
} ],
},
"kibana": [ {
"privileges": [ "all" ]
} ],
"kibana": {
"global": ["all"]
}
}
--------------------------------------------------
// KIBANA

==== Response

A successful call returns a response code of `204` and no response body.


==== Granting access to specific spaces
To grant access to individual spaces within {kib}, specify the space identifier within the `kibana` object.

Note: granting access

[source,js]
--------------------------------------------------
PUT /api/security/role/my_kibana_role
{
"metadata" : {
"version" : 1
},
"elasticsearch": {
"cluster" : [ "all" ],
"indices" : [ {
"names" : [ "index1", "index2" ],
"privileges" : [ "all" ],
"field_security" : {
"grant" : [ "title", "body" ]
},
"query" : "{\"match\": {\"title\": \"foo\"}}"
} ],
},
"kibana": {
"global": [],
"space": {
"marketing": ["all"],
"engineering": ["read"]
}
}
}
--------------------------------------------------
17 changes: 17 additions & 0 deletions docs/api/spaces-management.asciidoc
@@ -0,0 +1,17 @@
[role="xpack"]
[[spaces-api]]
== Kibana Spaces API

experimental[This API is *experimental* and may be changed or removed completely in a future release. The underlying Spaces concepts are stable, but the APIs for managing Spaces are currently experimental.]

The spaces API allows people to manage their spaces within {kib}.

* <<spaces-api-post>>
* <<spaces-api-put>>
* <<spaces-api-get>>
* <<spaces-api-delete>>

include::spaces-management/post.asciidoc[]
include::spaces-management/put.asciidoc[]
include::spaces-management/get.asciidoc[]
include::spaces-management/delete.asciidoc[]
25 changes: 25 additions & 0 deletions docs/api/spaces-management/delete.asciidoc
@@ -0,0 +1,25 @@
[[spaces-api-delete]]
=== Delete space

experimental[This API is *experimental* and may be changed or removed completely in a future release. The underlying Spaces concepts are stable, but the APIs for managing Spaces are currently experimental.]

[WARNING]
==================================================
Deleting a space will automatically delete all saved objects that belong to that space. This operation cannot be undone!
==================================================

==== Request

To delete a space, submit a DELETE request to the `/api/spaces/space/<space_id>`
endpoint:

[source,js]
--------------------------------------------------
DELETE /api/spaces/space/marketing
--------------------------------------------------
// KIBANA

==== Response

If the space is successfully deleted, the response code is `204`; otherwise, the response
code is 404.
77 changes: 77 additions & 0 deletions docs/api/spaces-management/get.asciidoc
@@ -0,0 +1,77 @@
[[spaces-api-get]]
=== Get Space

experimental[This API is *experimental* and may be changed or removed completely in a future release. The underlying Spaces concepts are stable, but the APIs for managing Spaces are currently experimental.]

Retrieves all {kib} spaces, or a specific space.

==== Get all {kib} spaces

===== Request

To retrieve all spaces, issue a GET request to the
/api/spaces/space endpoint.

[source,js]
--------------------------------------------------
GET /api/spaces/space
--------------------------------------------------
// KIBANA

===== Response

A successful call returns a response code of `200` and a response body containing a JSON
representation of the spaces.

[source,js]
--------------------------------------------------
[
{
"id": "default",
"name": "Default",
"description" : "This is the Default Space",
"_reserved": true
},
{
"id": "marketing",
"name": "Marketing",
"description" : "This is the Marketing Space",
"color": "#aabbcc",
"initials": "MK"
},
{
"id": "sales",
"name": "Sales",
"initials": "MK"
},
]
--------------------------------------------------

==== Get a specific space

===== Request

To retrieve a specific space, issue a GET request to
the `/api/spaces/space/<space_id>` endpoint:

[source,js]
--------------------------------------------------
GET /api/spaces/space/marketing
--------------------------------------------------
// KIBANA

===== Response

A successful call returns a response code of `200` and a response body containing a JSON
representation of the space.

[source,js]
--------------------------------------------------
{
"id": "marketing",
"name": "Marketing",
"description" : "This is the Marketing Space",
"color": "#aabbcc",
"initials": "MK"
}
--------------------------------------------------
50 changes: 50 additions & 0 deletions docs/api/spaces-management/post.asciidoc
@@ -0,0 +1,50 @@
[[spaces-api-post]]
=== Create Space

experimental[This API is *experimental* and may be changed or removed completely in a future release. The underlying Spaces concepts are stable, but the APIs for managing Spaces are currently experimental.]

Creates a new {kib} space. To update an existing space, use the PUT command.

==== Request

To create a space, issue a POST request to the
`/api/spaces/space` endpoint.

[source,js]
--------------------------------------------------
POST /api/spaces/space
--------------------------------------------------

==== Request Body

The following parameters can be specified in the body of a POST request to create a space:

`id`:: (string) Required identifier for the space. This identifier becomes part of Kibana's URL when inside the space. This cannot be changed by the update operation.

`name`:: (string) Required display name for the space.

`description`:: (string) Optional description for the space.

`initials`:: (string) Optionally specify the initials shown in the Space Avatar for this space. By default, the initials will be automatically generated from the space name.
If specified, initials should be either 1 or 2 characters.

`color`:: (string) Optioanlly specify the hex color code used in the Space Avatar for this space. By default, the color will be automatically generated from the space name.

===== Example

[source,js]
--------------------------------------------------
POST /api/spaces/space
{
"id": "marketing",
"name": "Marketing",
"description" : "This is the Marketing Space",
"color": "#aabbcc",
"initials": "MK"
}
--------------------------------------------------
// KIBANA

==== Response

A successful call returns a response code of `200` with the created Space.
50 changes: 50 additions & 0 deletions docs/api/spaces-management/put.asciidoc
@@ -0,0 +1,50 @@
[[spaces-api-put]]
=== Update Space

experimental[This API is *experimental* and may be changed or removed completely in a future release. The underlying Spaces concepts are stable, but the APIs for managing Spaces are currently experimental.]

Updates an existing {kib} space. To create a new space, use the POST command.

==== Request

To update a space, issue a PUT request to the
`/api/spaces/space/<space_id>` endpoint.

[source,js]
--------------------------------------------------
PUT /api/spaces/space/<space_id>
--------------------------------------------------

==== Request Body

The following parameters can be specified in the body of a PUT request to update a space:

`id`:: (string) Required identifier for the space. This identifier becomes part of Kibana's URL when inside the space. This cannot be changed by the update operation.

`name`:: (string) Required display name for the space.

`description`:: (string) Optional description for the space.

`initials`:: (string) Optionally specify the initials shown in the Space Avatar for this space. By default, the initials will be automatically generated from the space name.
If specified, initials should be either 1 or 2 characters.

`color`:: (string) Optioanlly specify the hex color code used in the Space Avatar for this space. By default, the color will be automatically generated from the space name.

===== Example

[source,js]
--------------------------------------------------
PUT /api/spaces/space/marketing
{
"id": "marketing",
"name": "Marketing",
"description" : "This is the Marketing Space",
"color": "#aabbcc",
"initials": "MK"
}
--------------------------------------------------
// KIBANA

==== Response

A successful call returns a response code of `200` with the updated Space.
6 changes: 3 additions & 3 deletions docs/development/core/development-basepath.asciidoc
Expand Up @@ -48,15 +48,15 @@ $http.get(chrome.addBasePath('/api/plugin/things'));
[float]
==== Server side

Append `config.get('server.basePath')` to any absolute URL path.
Append `request.getBasePath()` to any absolute URL path.

["source","shell"]
-----------
const basePath = server.config().get('server.basePath');
server.route({
path: '/redirect',
handler(req, reply) {
reply.redirect(`${basePath}/otherLocation`);
handler(request, reply) {
reply.redirect(`${request.getBasePath()}/otherLocation`);
}
});
-----------
Expand Down
2 changes: 2 additions & 0 deletions docs/index.asciidoc
Expand Up @@ -52,6 +52,8 @@ include::monitoring/index.asciidoc[]

include::management.asciidoc[]

include::spaces/index.asciidoc[]

include::security/index.asciidoc[]

include::management/watcher-ui/index.asciidoc[]
Expand Down
11 changes: 8 additions & 3 deletions docs/security/authorization/index.asciidoc
Expand Up @@ -2,10 +2,13 @@
[[xpack-security-authorization]]
=== Authorization

Authorizing users to use {kib} in most configurations is as simple as assigning the user
Authorizing users to use {kib} in simple configurations is as easy as assigning the user
either the `kibana_user` or `kibana_dashboard_only_user` reserved role. If you're running
a single tenant of {kib} against your {es} cluster, this is sufficient and no other
action is required.
a single tenant of {kib} against your {es} cluster, and you're not controlling access to individual spaces, then this is sufficient and no other action is required.

==== Spaces

If you want to control individual spaces in {kib}, do **not** use the `kibana_user` or `kibana_dashboard_only_user` roles. Users with these roles are able to access all spaces in Kibana. Instead, create your own roles that grant access to specific spaces.

==== Multi-tenant {kib}

Expand All @@ -15,6 +18,8 @@ either the *Management / Security / Roles* page in {kib} or the <<role-managemen
to assign a specific <<kibana-privileges, Kibana privilege>> at that tenant. After creating the
custom role, you should assign this role to the user(s) that you wish to have access.

While multi-tenant installations are supported, the recommended approach to securing access to segments of {kib} is to grant users access to specific spaces.

==== Legacy roles

Prior to {kib} 6.4, {kib} users required index privileges to the `kibana.index`
Expand Down
8 changes: 8 additions & 0 deletions docs/spaces/getting-started.asciidoc
@@ -0,0 +1,8 @@
[role="xpack"]
[[spaces-getting-started]]
=== Getting Started

Spaces are automatically enabled in {kib}. If you don't wish to use this feature, you can disable it
by setting `xpack.spaces.enabled` to `false` in your `kibana.yml` configuration file.

{kib} automatically creates a default space for you. If you are upgrading from another version of {kib}, then the default space will contain all of your existing saved objects. Although you can't delete the default space, you can customize it to your liking.
Binary file added docs/spaces/images/delete-space.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/spaces/images/edit-space.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/spaces/images/securing-spaces.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/spaces/images/space-management.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/spaces/images/space-selector.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions docs/spaces/index.asciidoc
@@ -0,0 +1,17 @@
[role="xpack"]
[[xpack-spaces]]
== Spaces

With spaces, you can organize your dashboards and other saved objects into meaningful categories.
After creating your own spaces, you will be asked to choose a space when you enter {kib}. Once inside a space,
you will only see the dashboards and other saved objects that belong to that space. You can change your active space at any time.

With security enabled, you can control which users have access to individual spaces.

[role="screenshot"]
image::spaces/images/space-selector.png["Space selector screen"]

include::getting-started.asciidoc[]
include::managing-spaces.asciidoc[]
include::securing-spaces.asciidoc[]
include::moving-saved-objects.asciidoc[]

0 comments on commit 748e711

Please sign in to comment.