conjur-asset-ui-beta 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. checksums.yaml +7 -0
  2. data/.git-hooks/pre_commit/trailing_whitespace.rb +26 -0
  3. data/.gitignore +23 -0
  4. data/.project +18 -0
  5. data/CHANGELOG.md +14 -0
  6. data/Gemfile +10 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +72 -0
  9. data/Rakefile +76 -0
  10. data/TODO.md +31 -0
  11. data/app/.csscomb.json +304 -0
  12. data/app/.jshintrc +46 -0
  13. data/app/build/css/bootstrap.css +6906 -0
  14. data/app/build/fonts/glyphicons-halflings-regular.eot +0 -0
  15. data/app/build/fonts/glyphicons-halflings-regular.svg +288 -0
  16. data/app/build/fonts/glyphicons-halflings-regular.ttf +0 -0
  17. data/app/build/fonts/glyphicons-halflings-regular.woff +0 -0
  18. data/app/build/fonts/glyphicons-halflings-regular.woff2 +0 -0
  19. data/app/build/images/conjur-logo.svg +26 -0
  20. data/app/build/images/icon-client-pc.svg +12 -0
  21. data/app/build/images/icon-environment.png +0 -0
  22. data/app/build/images/icon-person.svg +12 -0
  23. data/app/build/images/icon-policy.png +0 -0
  24. data/app/build/images/icon-resource.png +0 -0
  25. data/app/build/images/icon-service-dots.svg +13 -0
  26. data/app/build/images/icon-variable.png +0 -0
  27. data/app/build/index.html +26 -0
  28. data/app/build/js/app.js +78070 -0
  29. data/app/build/js/pace.js +2 -0
  30. data/app/config/preprocessor.js +9 -0
  31. data/app/config/webpack.js +84 -0
  32. data/app/gulpfile.js +144 -0
  33. data/app/package.json +83 -0
  34. data/app/src/actions.js +493 -0
  35. data/app/src/app.js +76 -0
  36. data/app/src/clients/audit.js +54 -0
  37. data/app/src/clients/generic.js +87 -0
  38. data/app/src/clients/layer_members.js +36 -0
  39. data/app/src/clients/list.js +82 -0
  40. data/app/src/clients/members.js +37 -0
  41. data/app/src/clients/search.js +19 -0
  42. data/app/src/components/app/__tests__/app-test.js +22 -0
  43. data/app/src/components/app/app.js +66 -0
  44. data/app/src/components/audit/__tests__/table_header-test.js +40 -0
  45. data/app/src/components/audit/box.js +11 -0
  46. data/app/src/components/audit/constants.js +7 -0
  47. data/app/src/components/audit/entry.js +107 -0
  48. data/app/src/components/audit/fields_mixin.js +13 -0
  49. data/app/src/components/audit/humanize_event.js +216 -0
  50. data/app/src/components/audit/table.js +100 -0
  51. data/app/src/components/audit/table_header.js +38 -0
  52. data/app/src/components/audit/timestamp.js +30 -0
  53. data/app/src/components/chart/chart.js +539 -0
  54. data/app/src/components/chart/chart_helper_mixin.js +79 -0
  55. data/app/src/components/custom/list.js +5 -0
  56. data/app/src/components/custom/view.js +71 -0
  57. data/app/src/components/dashboard/activity.js +113 -0
  58. data/app/src/components/dashboard/dashboard.js +47 -0
  59. data/app/src/components/flash/flash.js +17 -0
  60. data/app/src/components/generic/__tests__/time-test.js +43 -0
  61. data/app/src/components/generic/annotations.js +41 -0
  62. data/app/src/components/generic/breadcrumbs.js +59 -0
  63. data/app/src/components/generic/foldable_audit_section.js +252 -0
  64. data/app/src/components/generic/list.js +144 -0
  65. data/app/src/components/generic/list_factory.js +42 -0
  66. data/app/src/components/generic/resource_link.js +65 -0
  67. data/app/src/components/generic/role_link.js +65 -0
  68. data/app/src/components/generic/tab_mixin.js +148 -0
  69. data/app/src/components/generic/time.js +34 -0
  70. data/app/src/components/group/list.js +5 -0
  71. data/app/src/components/group/view.js +137 -0
  72. data/app/src/components/host/activity.js +93 -0
  73. data/app/src/components/host/details.js +30 -0
  74. data/app/src/components/host/host_link.js +20 -0
  75. data/app/src/components/host/list.js +5 -0
  76. data/app/src/components/host/view.js +113 -0
  77. data/app/src/components/layer/list.js +5 -0
  78. data/app/src/components/layer/view.js +180 -0
  79. data/app/src/components/navbar/__tests__/navbar-test.js +21 -0
  80. data/app/src/components/navbar/nav_search_form.js +41 -0
  81. data/app/src/components/navbar/navbar.js +71 -0
  82. data/app/src/components/owned_resources/owned_resources.js +86 -0
  83. data/app/src/components/owned_resources/owned_resources_box.js +106 -0
  84. data/app/src/components/permissions/permissions.js +143 -0
  85. data/app/src/components/permissions/permissions_table.js +104 -0
  86. data/app/src/components/policy/list.js +5 -0
  87. data/app/src/components/policy/view.js +98 -0
  88. data/app/src/components/refresh/refresh.js +30 -0
  89. data/app/src/components/refresh/refresh.less +15 -0
  90. data/app/src/components/search/group.js +45 -0
  91. data/app/src/components/search/group_heading.js +50 -0
  92. data/app/src/components/search/group_title.js +38 -0
  93. data/app/src/components/search/result_item.js +57 -0
  94. data/app/src/components/search/search.js +103 -0
  95. data/app/src/components/user/activity.js +92 -0
  96. data/app/src/components/user/details.js +30 -0
  97. data/app/src/components/user/list.js +5 -0
  98. data/app/src/components/user/pubkeys.js +116 -0
  99. data/app/src/components/user/pubkeys.less +56 -0
  100. data/app/src/components/user/view.js +123 -0
  101. data/app/src/components/variable/activity.js +83 -0
  102. data/app/src/components/variable/details.js +48 -0
  103. data/app/src/components/variable/fetchers.js +83 -0
  104. data/app/src/components/variable/list.js +5 -0
  105. data/app/src/components/variable/updaters.js +83 -0
  106. data/app/src/components/variable/view.js +105 -0
  107. data/app/src/constants.js +35 -0
  108. data/app/src/images/conjur-logo.svg +26 -0
  109. data/app/src/images/icon-client-pc.svg +12 -0
  110. data/app/src/images/icon-environment.png +0 -0
  111. data/app/src/images/icon-person.svg +12 -0
  112. data/app/src/images/icon-policy.png +0 -0
  113. data/app/src/images/icon-resource.png +0 -0
  114. data/app/src/images/icon-service-dots.svg +13 -0
  115. data/app/src/images/icon-variable.png +0 -0
  116. data/app/src/pages/index.html +26 -0
  117. data/app/src/routes.js +57 -0
  118. data/app/src/stores/app_store.js +29 -0
  119. data/app/src/stores/audit_store.js +77 -0
  120. data/app/src/stores/group_store.js +105 -0
  121. data/app/src/stores/host_store.js +98 -0
  122. data/app/src/stores/layer_store.js +115 -0
  123. data/app/src/stores/policy_store.js +89 -0
  124. data/app/src/stores/resources_store.js +118 -0
  125. data/app/src/stores/route_store.js +24 -0
  126. data/app/src/stores/search_store.js +73 -0
  127. data/app/src/stores/user_store.js +111 -0
  128. data/app/src/stores/variable_store.js +94 -0
  129. data/app/src/styles/bootstrap.less +56 -0
  130. data/app/src/styles/styles.less +634 -0
  131. data/app/src/utils.js +43 -0
  132. data/app/src/vendor/pace.js +2 -0
  133. data/conjur-asset-ui.gemspec +36 -0
  134. data/features/navigation_bar.feature +31 -0
  135. data/features/step_definitions/custom_step.rb +32 -0
  136. data/features/support/env.rb +38 -0
  137. data/features/support/hooks.rb +30 -0
  138. data/features/support/world.rb +17 -0
  139. data/lib/conjur-asset-ui-version.rb +7 -0
  140. data/lib/conjur-asset-ui.rb +7 -0
  141. data/lib/conjur/command/ui.rb +54 -0
  142. data/lib/conjur/webserver/api_proxy.rb +94 -0
  143. data/lib/conjur/webserver/authorize.rb +28 -0
  144. data/lib/conjur/webserver/conjur_info.rb +33 -0
  145. data/lib/conjur/webserver/home.rb +42 -0
  146. data/lib/conjur/webserver/login.rb +57 -0
  147. data/lib/conjur/webserver/renderer.rb +34 -0
  148. data/lib/conjur/webserver/server.rb +130 -0
  149. data/public/js/views/roleGraph.js +91 -0
  150. metadata +373 -0
@@ -0,0 +1,65 @@
1
+ 'use strict';
2
+
3
+ var React = require('react'),
4
+ Link = require('react-router').Link,
5
+ includes = require('lodash/collection/includes');
6
+
7
+ /** render a link to the role represented by this.props.id
8
+ Example: <RoleLink id='ci:user:jon'/>
9
+ */
10
+ module.exports = React.createClass({
11
+ displayName: 'RoleLink',
12
+
13
+ knownTypes: ['user', 'group', 'layer', 'host', 'policy'],
14
+
15
+ render() {
16
+ // guard against misuse such as https://github.com/conjurinc/conjur-asset-ui/issues/78
17
+ if (this.props.id === undefined) {
18
+ return (
19
+ <em>undefined</em>
20
+ );
21
+ }
22
+
23
+ var tokens = this.props.id.split(':'),
24
+ kind = tokens[1],
25
+ id = tokens[tokens.length-1];
26
+
27
+ if (tokens.length === 1) { // just username
28
+ kind = 'user';
29
+ id = tokens[0];
30
+ }
31
+
32
+ var kindIsKnown = includes(this.knownTypes, kind),
33
+ text = id,
34
+ classes = ['role-link'];
35
+
36
+ if (!this.props.noIcon) {
37
+ if (kindIsKnown) {
38
+ classes.push(kind);
39
+ } else {
40
+ classes.push('abstract'); // we have no picture for abstract role yet
41
+
42
+ if (text === id) {
43
+ text = [kind, text].join(':'); // prepend kind to id
44
+ }
45
+ }
46
+ } else if (text === id) {
47
+ text = [kind, text].join(':'); // prepend kind to id
48
+ }
49
+
50
+ if (kindIsKnown) {
51
+ return (
52
+ <Link to={kind} params={{id: window.encodeURIComponent(id)}}
53
+ className={classes.join(' ')} style={this.props.style}>
54
+ {text}
55
+ </Link>
56
+ );
57
+ } else {
58
+ return (
59
+ <a href="#" className={classes.join(' ')} style={this.props.style}>
60
+ {text}
61
+ </a>
62
+ );
63
+ }
64
+ }
65
+ });
@@ -0,0 +1,148 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+
5
+ var TabPane = require('react-bootstrap/lib/TabPane');
6
+
7
+ var OwnedResources = require('../owned_resources/owned_resources'),
8
+ RoleLink = require('./role_link'),
9
+ Annotations = require('./annotations'),
10
+ Permissions = require('../permissions/permissions'),
11
+ utils = require('../../utils');
12
+
13
+ var includes = require('lodash/collection/includes');
14
+
15
+ // we can't create custom TabPanes as components,
16
+ // because they won't be recognized by TabbedArea as tabs
17
+ // that's why we just generalize their constructors
18
+ module.exports = {
19
+ containsOwnedItems() {
20
+ return (
21
+ this.state.owned &&
22
+ this.state.owned.length &&
23
+ this.state.owned.length > 0
24
+ );
25
+ },
26
+
27
+ ownedIds() {
28
+ if (this.containsOwnedItems()) {
29
+ return this.state.owned.map(function(ownedItem) {
30
+ return ownedItem.id;
31
+ });
32
+ }
33
+
34
+ return [];
35
+ },
36
+
37
+ ownedTab() {
38
+ if (this.containsOwnedItems()) {
39
+ var result = (
40
+ <TabPane eventKey="owned"
41
+ tab={utils.getTabname('Owned resources', this.state.owned)}>
42
+ <OwnedResources owned={this.state.owned} tabview={true} />
43
+ </TabPane>
44
+ );
45
+
46
+ return result;
47
+ }
48
+
49
+ return null;
50
+ },
51
+
52
+ containsNontrivialRoles() {
53
+ return (
54
+ this.state.roles &&
55
+ this.state.roles.length &&
56
+ this.state.roles.length > 1
57
+ );
58
+ },
59
+
60
+ externalRoles(ownId) {
61
+ if (!this.containsNontrivialRoles()) {
62
+ return [];
63
+ }
64
+
65
+ var ownedIds = this.ownedIds();
66
+
67
+ return this.state.roles.filter(function(roleid) {
68
+ // do not show themself
69
+ return roleid !== ownId;
70
+ }).filter(function(roleid) {
71
+ // do not show internal roles
72
+ return roleid.split(':')[1] !== '@';
73
+ }).filter(function(roleid) {
74
+ // do not show owned groups
75
+ return ! includes(ownedIds, roleid);
76
+ });
77
+ },
78
+
79
+ membershipsTab(ownId) {
80
+ if (!this.containsNontrivialRoles()) {
81
+ return null;
82
+ }
83
+
84
+ var membershipLinks = this.externalRoles(ownId)
85
+ .sort(function(a, b) {
86
+ return a.toLowerCase().localeCompare(b.toLowerCase());
87
+ })
88
+ .map(function(roleid) {
89
+ return (
90
+ <li className="list-group-item list-group-item-noborder">
91
+ <RoleLink id={roleid} />
92
+ </li>
93
+ );
94
+ });
95
+
96
+ if (membershipLinks.length === 0) {
97
+ return null;
98
+ }
99
+
100
+ var result = (
101
+ <TabPane eventKey="memberships"
102
+ tab={utils.getTabname('Explicit memberships', membershipLinks)}>
103
+ <ul className="list-group">
104
+ {membershipLinks}
105
+ </ul>
106
+ </TabPane>
107
+ );
108
+
109
+ return result;
110
+ },
111
+
112
+ containsAnnotations() {
113
+ return (
114
+ this.state.annotations &&
115
+ this.state.annotations.length &&
116
+ this.state.annotations.length > 0
117
+ );
118
+ },
119
+
120
+ annotationsTab() {
121
+ if (!this.containsAnnotations()) {
122
+ return null;
123
+ }
124
+
125
+ var result = (
126
+ <TabPane eventKey="annotations"
127
+ tab={utils.getTabname('Annotations', this.state.annotations)}>
128
+ <Annotations annotations={this.state.annotations} />
129
+ </TabPane>
130
+ );
131
+
132
+ return result;
133
+ },
134
+
135
+ permissionsTab(roleid) {
136
+ var result = (
137
+ <TabPane eventKey="permissions" tab="Permissions held">
138
+ <Permissions owned={this.state.owned}
139
+ roles={this.state.roles}
140
+ role={roleid}
141
+ resources={this.state.resources}
142
+ tabview={true} />
143
+ </TabPane>
144
+ );
145
+
146
+ return result;
147
+ }
148
+ };
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+
3
+ var React = require('react'),
4
+ moment = require('moment');
5
+
6
+ // render a <time> tag. props.timestamp should be a timestamp
7
+ // that moment can parse, props.format should be one of the
8
+ // format strings accepted by moment (optional).
9
+ module.exports = React.createClass({
10
+ displayName: 'Time',
11
+
12
+ render() {
13
+ var timestamp = this.props.timestamp,
14
+ tsFormat = this.props.tsFormat || '',
15
+ format = this.props.format || 'lll',
16
+ m = moment(timestamp, tsFormat),
17
+ date;
18
+
19
+ switch (this.props.relative) {
20
+ case 'time from now':
21
+ date = m.fromNow();
22
+ break;
23
+ case 'calendar time':
24
+ date = m.calendar();
25
+ break;
26
+ default:
27
+ date = m.format(format);
28
+ }
29
+
30
+ return (
31
+ <time dateTime={m.format()}>{date}</time>
32
+ );
33
+ }
34
+ });
@@ -0,0 +1,5 @@
1
+ 'use strict';
2
+
3
+ var genericListFactory = require('../generic/list_factory');
4
+
5
+ module.exports = genericListFactory('group', 'Groups');
@@ -0,0 +1,137 @@
1
+ 'use strict';
2
+
3
+ var React = require('react'),
4
+ Fluxxor = require('fluxxor'),
5
+ FluxMixin = Fluxxor.FluxMixin(React),
6
+ StoreWatchMixin = Fluxxor.StoreWatchMixin,
7
+ Router = require('react-router');
8
+
9
+ var TabbedArea = require('react-bootstrap/lib/TabbedArea'),
10
+ TabPane = require('react-bootstrap/lib/TabPane');
11
+
12
+ var TabMixin = require('../generic/tab_mixin'),
13
+ Breadcrumbs = require('../generic/breadcrumbs'),
14
+ RoleLink = require('../generic/role_link'),
15
+ utils = require('../../utils');
16
+
17
+ var AuditTable = require('../audit/table');
18
+
19
+ var compact = require('lodash/array/compact'),
20
+ map = require('lodash/collection/map');
21
+
22
+ module.exports = React.createClass({
23
+ displayName: 'GroupView',
24
+
25
+ mixins: [FluxMixin, StoreWatchMixin('audit', 'group', 'resources'), Router.State, TabMixin],
26
+
27
+ getStateFromFlux() {
28
+ var flux = this.getFlux(),
29
+ id = unescape(this.getParams().id),
30
+ audit = flux.store('audit').getEventFor('group', id),
31
+ data = flux.store('group').getData(),
32
+ resource = flux.store('resources').getResource('group', id);
33
+
34
+ var ret = {
35
+ loading: data.loading,
36
+ group: data.group,
37
+ owned: data.owned,
38
+ roles: data.roles,
39
+ owner: resource.data.owner,
40
+ annotations: resource.data.annotations,
41
+ members: data.members,
42
+ resources: data.resources,
43
+ audit: audit.data
44
+ };
45
+
46
+ ret.loading.audit = audit.loading;
47
+ ret.loading.owner = resource.loading;
48
+ ret.loading.annotations = resource.loading;
49
+
50
+ return ret;
51
+ },
52
+
53
+ render() {
54
+ var members = map(this.state.members, function(member) {
55
+ return (
56
+ <li className="list-group-item list-group-item-noborder">
57
+ <RoleLink id={member.member} />
58
+ </li>
59
+ );
60
+ });
61
+
62
+ // TODO: refactor (common code should go to mixins)
63
+ // TODO: memberships
64
+ // TODO: controls for members management
65
+ var overviewTab =(
66
+ <TabPane eventKey="overview" tab="Overview">
67
+ <dl className="dl-horizontal">
68
+ <dt>Created by</dt>
69
+ <dd><RoleLink id={this.state.group.userid} /></dd>
70
+
71
+ <dt>Owner</dt>
72
+ <dd><RoleLink id={this.state.owner} /></dd>
73
+ </dl>
74
+ </TabPane>
75
+ );
76
+
77
+ var membersTab = (
78
+ <TabPane eventKey="members" tab={utils.getTabname('Members', members)}>
79
+ <ul className="list-group">
80
+ {members}
81
+ </ul>
82
+ </TabPane>
83
+ );
84
+
85
+ var ownedTab = this.ownedTab(),
86
+ membershipsTab = this.membershipsTab(this.state.group.roleid),
87
+ permissionsTab = this.permissionsTab(this.state.group.roleid),
88
+ annotationsTab = this.annotationsTab();
89
+
90
+ var auditTab = (
91
+ <TabPane eventKey="audit" tab="Recent Activity">
92
+ <div className="audit auditGroup">
93
+ <AuditTable events={this.state.audit}
94
+ caption=""
95
+ isLoading={this.state.loading.audit} />
96
+ </div>
97
+ </TabPane>
98
+ );
99
+
100
+ var tabs = compact([
101
+ overviewTab,
102
+ membersTab,
103
+ ownedTab,
104
+ membershipsTab,
105
+ permissionsTab,
106
+ annotationsTab,
107
+ auditTab
108
+ ]);
109
+
110
+ var elems = [
111
+ {url: '/ui/groups', text: 'Groups'},
112
+ {url: '', text: this.state.group.id},
113
+ {url: '', text: '(owned by ' + this.state.group.ownerid + ')'}
114
+ ];
115
+
116
+ return (
117
+ <div className="b-group">
118
+ <Breadcrumbs elems={elems} />
119
+ <hr />
120
+ <h2>Group {this.state.group.id}</h2>
121
+ <TabbedArea defaultActiveKey="overview">
122
+ {tabs}
123
+ </TabbedArea>
124
+ </div>
125
+ );
126
+ },
127
+
128
+ componentDidMount() {
129
+ var actions = this.getFlux().actions,
130
+ id = unescape(this.getParams().id);
131
+
132
+ actions.audit.loadForRole('group', id);
133
+ actions.audit.loadForResource('group', id);
134
+ actions.group.load(id);
135
+ actions.resources.loadOne('group', id);
136
+ }
137
+ });
@@ -0,0 +1,93 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+
5
+ var Chart = require('../chart/chart'),
6
+ ChartHelperMixin = require('../chart/chart_helper_mixin'),
7
+ Refresh = require('../refresh/refresh');
8
+
9
+ module.exports = React.createClass({
10
+ displayName: 'HostActivity',
11
+
12
+ mixins: [ChartHelperMixin],
13
+
14
+ getDefaultProps() {
15
+ return {
16
+ options: {
17
+ legend: {
18
+ logins: 'Logins to host',
19
+ sudo: 'Sudo calls on host',
20
+ reads: 'Secret reads by host',
21
+ updates: 'Secret updates by host',
22
+ warnings: 'Warnings'
23
+ },
24
+ axis: {
25
+ y: {
26
+ label: 'Value'
27
+ }
28
+ }
29
+ }
30
+ };
31
+ },
32
+
33
+ getDefaultItem() {
34
+ return {
35
+ logins: 0,
36
+ sudo: 0,
37
+ reads: 0,
38
+ updates: 0,
39
+ warnings: 0
40
+ };
41
+ },
42
+
43
+ getEventType(e) {
44
+ if (e.hasOwnProperty('facility') && e.facility === 'ssh') {
45
+ if (e.action === 'sudo' && e.allowed === true) {
46
+ return 'sudo';
47
+ }
48
+
49
+ if (e.action === 'login' && e.allowed === true) {
50
+ return 'logins';
51
+ }
52
+ }
53
+
54
+ if (e.hasOwnProperty('error') || e.allowed === false) {
55
+ return 'warnings';
56
+ }
57
+
58
+ if (e.action === 'check' &&
59
+ e.hasOwnProperty('allowed') &&
60
+ e.allowed === true) {
61
+
62
+ if (e.hasOwnProperty('role') && e.role === this.props.roleid) {
63
+ if (e.hasOwnProperty('resource') &&
64
+ e.resource.split(':')[1] === 'variable') {
65
+
66
+ if (e.privilege === 'update') {
67
+ return 'updates';
68
+ }
69
+
70
+ if (e.privilege === 'execute') {
71
+ return 'reads';
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ return null;
78
+ },
79
+
80
+ render() {
81
+ var data = this.getData(this.props.audit);
82
+
83
+ return (
84
+ <div className="b-host-activity">
85
+ <h2>Activity<Refresh show={this.props.isLoading} /></h2>
86
+ <div className="b-host-activity__graph">
87
+ <Chart options={this.props.options}
88
+ data={data} />
89
+ </div>
90
+ </div>
91
+ );
92
+ }
93
+ });