-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature Controls - Infrastructure and Logging #31843
Merged
kobelb
merged 43 commits into
elastic:granular-app-privileges
from
kobelb:fc/enable-infra
Mar 11, 2019
Merged
Changes from 2 commits
Commits
Show all changes
43 commits
Select commit
Hold shift + click to select a range
a500ded
hide infra/logs apps if disabled via UICapabilities
legrego 2df2771
adds tests
legrego b1b7c41
adds UICapability tests for infra and log apps
legrego fe63b39
update expected privilege/action mapping
legrego bbfc833
adds feature controls security tests for infraHome
legrego e44b0eb
adds infra spaces feature control tests
legrego f5db5c8
remove debug code
legrego 4794cbf
Merge branch 'granular-app-privileges' into fc/enable-infra
legrego a1c3345
a sample readonly implementation, ignoring 'logs' privileges
legrego 0382915
Merge branch 'granular-app-privileges' into fc/enable-infra
legrego 0cb1bf0
ts fixes
legrego dc61811
fix capability expectations
legrego 7981ad4
Removing RequiresUICapability component, since there are no usages
kobelb d3bd237
Driving the source configuration seperately for logs/infrastructure
kobelb 19bfb58
Adding infrastructure feature controls security functional tests
kobelb 8eab19a
Adding spaces infrastructure tests
kobelb b20b6b6
Adding logs functional tests
kobelb d9dcccf
Reworking the ui capability tests to be more consistent
kobelb daea6a3
Fixing privileges API
kobelb f53271c
Forcing logout
kobelb 7b5be17
Merge remote-tracking branch 'upstream/granular-app-privileges' into …
kobelb 1b3b418
Fixing comma issue introduced by merge
kobelb a51083d
Fix merge conflicts and loading/unloading esarchives more consistently
kobelb fc1ea1a
Removing unnecessary !!
kobelb 11e0082
Merge remote-tracking branch 'upstream/granular-app-privileges' into …
kobelb c213bc3
Fixing saved object management tests
kobelb 6202363
Fixing more tests
kobelb 4776f1f
Using the new context APIs
kobelb ab1e930
Revert "Using the new context APIs"
kobelb 28612d0
Adding future version of ui capabilities react provider
kobelb d4d8538
Merge remote-tracking branch 'upstream/granular-app-privileges' into …
kobelb 7bdc00d
Merge remote-tracking branch 'upstream/granular-app-privileges' into …
kobelb a8d9c41
Switching the order of the HOC's for infra and making the future the
kobelb 35829c1
Applying Felix's PR feedback
kobelb 2cd4a50
Protecting Infra's GraphQL APIs
kobelb 8e49558
Updating privileges list
kobelb 20cd128
Using the introspection query
kobelb c41d5b3
No longer using apollo context library, rephrasing test descriptions
kobelb 14510b2
Merge remote-tracking branch 'upstream/granular-app-privileges' into …
kobelb 9206566
Fixing issue introduced by merge conflict, I forgot a }
kobelb b4b987f
Merge remote-tracking branch 'upstream/granular-app-privileges' into …
kobelb 6371bb6
Putting back missplaced data test subj
kobelb a071707
Merge remote-tracking branch 'upstream/granular-app-privileges' into …
kobelb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
294 changes: 294 additions & 0 deletions
294
x-pack/test/api_integration/apis/infra/feature_controls.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,294 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import expect from 'expect.js'; | ||
import { SecurityService, SpacesService } from 'x-pack/test/common/services'; | ||
import { metadataQuery } from '../../../../plugins/infra/public/containers/metadata/metadata.gql_query'; | ||
import { MetadataQuery } from '../../../../plugins/infra/public/graphql/types'; | ||
import { KbnTestProvider } from './types'; | ||
|
||
// tslint:disable:no-default-export | ||
const featureControlsTests: KbnTestProvider = ({ getService }) => { | ||
const supertest = getService('supertestWithoutAuth'); | ||
const security: SecurityService = getService('security'); | ||
const spaces: SpacesService = getService('spaces'); | ||
const clientFactory = getService('infraOpsGraphQLClientFactory'); | ||
|
||
const expectGraphQL404 = (result: any) => { | ||
expect(result.response).to.be(undefined); | ||
expect(result.error).not.to.be(undefined); | ||
expect(result.error).to.have.property('networkError'); | ||
expect(result.error.networkError).to.have.property('statusCode', 404); | ||
}; | ||
|
||
const expectGraphQLResponse = (result: any) => { | ||
expect(result.error).to.be(undefined); | ||
expect(result.response).to.have.property('data'); | ||
expect(result.response.data).to.be.an('object'); | ||
}; | ||
|
||
const expectGraphIQL404 = (result: any) => { | ||
expect(result.error).to.be(undefined); | ||
expect(result.response).not.to.be(undefined); | ||
expect(result.response).to.have.property('statusCode', 404); | ||
}; | ||
|
||
const expectGraphIQLResponse = (result: any) => { | ||
expect(result.error).to.be(undefined); | ||
expect(result.response).not.to.be(undefined); | ||
expect(result.response).to.have.property('statusCode', 200); | ||
}; | ||
|
||
const executeGraphQLQuery = async (username: string, password: string, spaceId?: string) => { | ||
const queryOptions = { | ||
query: metadataQuery, | ||
variables: { | ||
sourceId: 'default', | ||
nodeId: 'demo-stack-mysql-01', | ||
nodeType: 'host', | ||
}, | ||
}; | ||
|
||
const basePath = spaceId ? `/s/${spaceId}` : ''; | ||
|
||
const client = clientFactory({ username, password, basePath }); | ||
let error; | ||
let response; | ||
try { | ||
response = await client.query<MetadataQuery.Query>(queryOptions); | ||
} catch (err) { | ||
error = err; | ||
} | ||
return { | ||
error, | ||
response, | ||
}; | ||
}; | ||
|
||
const executeGraphIQLRequest = async (username: string, password: string, spaceId?: string) => { | ||
const basePath = spaceId ? `/s/${spaceId}` : ''; | ||
|
||
return supertest | ||
.get(`${basePath}/api/infra/graphql/graphiql`) | ||
.auth(username, password) | ||
.then((response: any) => ({ error: undefined, response })) | ||
.catch((error: any) => ({ error, response: undefined })); | ||
}; | ||
|
||
describe('feature controls', () => { | ||
it(`APIs can't be accessed by logstash-* read privileges role`, async () => { | ||
const username = 'logstash_read'; | ||
const roleName = 'logstash_read'; | ||
const password = `${username}-password`; | ||
try { | ||
await security.role.create(roleName, { | ||
elasticsearch: { | ||
indices: [ | ||
{ | ||
names: ['logstash-*'], | ||
privileges: ['read', 'view_index_metadata'], | ||
}, | ||
], | ||
}, | ||
}); | ||
|
||
await security.user.create(username, { | ||
password, | ||
roles: [roleName], | ||
full_name: 'a kibana user', | ||
}); | ||
|
||
const graphQLResult = await executeGraphQLQuery(username, password); | ||
expectGraphQL404(graphQLResult); | ||
|
||
const graphQLIResult = await executeGraphIQLRequest(username, password); | ||
expectGraphIQL404(graphQLIResult); | ||
} finally { | ||
await security.role.delete(roleName); | ||
await security.user.delete(username); | ||
} | ||
}); | ||
|
||
it('APIs can be accessed global all with logstash-* read privileges role', async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm having a hard time parsing this name and forming a model of what the test case is supposed to do. 🤔 Could it be rephrased somehow? |
||
const username = 'global_all'; | ||
const roleName = 'global_all'; | ||
const password = `${username}-password`; | ||
try { | ||
await security.role.create(roleName, { | ||
elasticsearch: { | ||
indices: [ | ||
{ | ||
names: ['logstash-*'], | ||
privileges: ['read', 'view_index_metadata'], | ||
}, | ||
], | ||
}, | ||
kibana: [ | ||
{ | ||
base: ['all'], | ||
spaces: ['*'], | ||
}, | ||
], | ||
}); | ||
|
||
await security.user.create(username, { | ||
password, | ||
roles: [roleName], | ||
full_name: 'a kibana user', | ||
}); | ||
|
||
const graphQLResult = await executeGraphQLQuery(username, password); | ||
expectGraphQLResponse(graphQLResult); | ||
|
||
const graphQLIResult = await executeGraphIQLRequest(username, password); | ||
expectGraphIQLResponse(graphQLIResult); | ||
} finally { | ||
await security.role.delete(roleName); | ||
await security.user.delete(username); | ||
} | ||
}); | ||
|
||
// this could be any role which doesn't have access to the infra feature | ||
it(`APIs can't be accessed by dashboard all with logstash-* read privileges role`, async () => { | ||
const username = 'dashboard_all'; | ||
const roleName = 'dashboard_all'; | ||
const password = `${username}-password`; | ||
try { | ||
await security.role.create(roleName, { | ||
elasticsearch: { | ||
indices: [ | ||
{ | ||
names: ['logstash-*'], | ||
privileges: ['read', 'view_index_metadata'], | ||
}, | ||
], | ||
}, | ||
kibana: [ | ||
{ | ||
feature: { | ||
dashboard: ['all'], | ||
}, | ||
spaces: ['*'], | ||
}, | ||
], | ||
}); | ||
|
||
await security.user.create(username, { | ||
password, | ||
roles: [roleName], | ||
full_name: 'a kibana user', | ||
}); | ||
|
||
const graphQLResult = await executeGraphQLQuery(username, password); | ||
expectGraphQL404(graphQLResult); | ||
|
||
const graphQLIResult = await executeGraphIQLRequest(username, password); | ||
expectGraphIQL404(graphQLIResult); | ||
} finally { | ||
await security.role.delete(roleName); | ||
await security.user.delete(username); | ||
} | ||
}); | ||
|
||
describe('spaces', () => { | ||
// the following tests create a user_1 which has infrastructure read access to space_1, logs read access to space_2 and dashboard all access to space_3 | ||
const space1Id = 'space_1'; | ||
const space2Id = 'space_2'; | ||
const space3Id = 'space_3'; | ||
|
||
const roleName = 'user_1'; | ||
const username = 'user_1'; | ||
const password = 'user_1-password'; | ||
|
||
before(async () => { | ||
await spaces.create({ | ||
id: space1Id, | ||
name: space1Id, | ||
disabledFeatures: [], | ||
}); | ||
await spaces.create({ | ||
id: space2Id, | ||
name: space2Id, | ||
disabledFeatures: [], | ||
}); | ||
await spaces.create({ | ||
id: space3Id, | ||
name: space3Id, | ||
disabledFeatures: [], | ||
}); | ||
await security.role.create(roleName, { | ||
elasticsearch: { | ||
indices: [ | ||
{ | ||
names: ['logstash-*'], | ||
privileges: ['read', 'view_index_metadata'], | ||
}, | ||
], | ||
}, | ||
kibana: [ | ||
{ | ||
feature: { | ||
infrastructure: ['read'], | ||
}, | ||
spaces: [space1Id], | ||
}, | ||
{ | ||
feature: { | ||
logs: ['read'], | ||
}, | ||
spaces: [space2Id], | ||
}, | ||
{ | ||
feature: { | ||
dashboard: ['all'], | ||
}, | ||
spaces: [space3Id], | ||
}, | ||
], | ||
}); | ||
await security.user.create(username, { | ||
password, | ||
roles: [roleName], | ||
}); | ||
}); | ||
|
||
after(async () => { | ||
await spaces.delete(space1Id); | ||
await spaces.delete(space2Id); | ||
await spaces.delete(space3Id); | ||
await security.role.delete(roleName); | ||
await security.user.delete(username); | ||
}); | ||
|
||
it('user_1 can access APIs in space_1', async () => { | ||
const graphQLResult = await executeGraphQLQuery(username, password, space1Id); | ||
expectGraphQLResponse(graphQLResult); | ||
|
||
const graphQLIResult = await executeGraphIQLRequest(username, password, space1Id); | ||
expectGraphIQLResponse(graphQLIResult); | ||
}); | ||
|
||
it(`user_1 can access APIs in space_2`, async () => { | ||
const graphQLResult = await executeGraphQLQuery(username, password, space2Id); | ||
expectGraphQLResponse(graphQLResult); | ||
|
||
const graphQLIResult = await executeGraphIQLRequest(username, password, space2Id); | ||
expectGraphIQLResponse(graphQLIResult); | ||
}); | ||
|
||
it(`user_1 can't access APIs in space_3`, async () => { | ||
const graphQLResult = await executeGraphQLQuery(username, password, space3Id); | ||
expectGraphQL404(graphQLResult); | ||
|
||
const graphQLIResult = await executeGraphIQLRequest(username, password, space3Id); | ||
expectGraphIQL404(graphQLIResult); | ||
}); | ||
}); | ||
}); | ||
}; | ||
|
||
// tslint:disable-next-line no-default-export | ||
export default featureControlsTests; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be interested to learn how these tags map to the
infra/graphql
privileges. Are they associated via the overlappinggraphql
part? What are the semantics ofaccess
and are there alternative values for it?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is rather clumsy at the moment and something that I'd like to address in a subsequent PR. The following code parses the access tag to construct the action which we then ensure the user has privileges for:
kibana/x-pack/plugins/security/index.js
Lines 248 to 260 in 74d5df2
It's hard to explain in words or pseudo-code, hence the desire to refactor it :)