conjur-asset-ui 1.4.2 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (211) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -3
  3. data/CHANGELOG.md +10 -0
  4. data/Makefile +19 -0
  5. data/README.md +0 -3
  6. data/Rakefile +22 -17
  7. data/TODO.md +0 -23
  8. data/app/.csscomb.json +304 -0
  9. data/app/.eslintignore +3 -0
  10. data/app/.eslintrc +265 -0
  11. data/app/config/preprocessor.js +19 -0
  12. data/app/config/webpack.js +124 -0
  13. data/app/gulpfile.js +96 -0
  14. data/app/package.json +86 -0
  15. data/app/src/actions.js +550 -0
  16. data/app/src/app.js +83 -0
  17. data/app/src/clients/audit.js +34 -0
  18. data/app/src/clients/auth.js +24 -0
  19. data/app/src/clients/generic.js +52 -0
  20. data/app/src/clients/graph.js +7 -0
  21. data/app/src/clients/layer_members.js +18 -0
  22. data/app/src/clients/list.js +31 -0
  23. data/app/src/clients/members.js +20 -0
  24. data/app/src/clients/request.js +531 -0
  25. data/app/src/clients/search.js +5 -0
  26. data/app/src/components/app/__tests__/app-test.js +22 -0
  27. data/app/src/components/app/app.js +36 -0
  28. data/app/src/components/app/wrapper.js +17 -0
  29. data/app/src/components/audit/__tests__/table_header-test.js +22 -0
  30. data/app/src/components/audit/box.js +9 -0
  31. data/app/src/components/audit/constants.js +5 -0
  32. data/app/src/components/audit/entry.js +105 -0
  33. data/app/src/components/audit/fields_mixin.js +11 -0
  34. data/app/src/components/audit/humanize_event.js +213 -0
  35. data/app/src/components/audit/table.js +64 -0
  36. data/app/src/components/audit/table_header.js +37 -0
  37. data/app/src/components/audit/timestamp.js +28 -0
  38. data/app/src/components/auth/login.js +177 -0
  39. data/app/src/components/auth/login.less +71 -0
  40. data/app/src/components/auth/logout.js +42 -0
  41. data/app/src/components/auth/logout.less +21 -0
  42. data/app/src/components/chart/chart.js +540 -0
  43. data/app/src/components/chart/chart_helper_mixin.js +78 -0
  44. data/app/src/components/custom/list.js +3 -0
  45. data/app/src/components/custom/view.js +81 -0
  46. data/app/src/components/dashboard/activity.js +144 -0
  47. data/app/src/components/dashboard/dashboard.js +46 -0
  48. data/app/src/components/flash/flash.js +98 -0
  49. data/app/src/components/flash/flash.less +3 -0
  50. data/app/src/components/generic/__tests__/time-test.js +42 -0
  51. data/app/src/components/generic/annotations.js +39 -0
  52. data/app/src/components/generic/breadcrumbs.js +57 -0
  53. data/app/src/components/generic/foldable_audit_section.js +204 -0
  54. data/app/src/components/generic/list.js +141 -0
  55. data/app/src/components/generic/list_factory.js +41 -0
  56. data/app/src/components/generic/resource_link.js +64 -0
  57. data/app/src/components/generic/role_link.js +66 -0
  58. data/app/src/components/generic/tab_mixin.js +146 -0
  59. data/app/src/components/generic/time.js +32 -0
  60. data/app/src/components/graph/__tests__/collapse-test.js +133 -0
  61. data/app/src/components/graph/__tests__/edges-from-vertices-test.js +48 -0
  62. data/app/src/components/graph/__tests__/new-vertex-set-test.js +16 -0
  63. data/app/src/components/graph/__tests__/next-id-test.js +27 -0
  64. data/app/src/components/graph/__tests__/role-kind-from-id-test.js +24 -0
  65. data/app/src/components/graph/__tests__/vertices-from-edges-test.js +72 -0
  66. data/app/src/components/graph/graph.js +449 -0
  67. data/app/src/components/graph/graph.less +39 -0
  68. data/app/src/components/graph/helpers.js +368 -0
  69. data/app/src/components/group/list.js +3 -0
  70. data/app/src/components/group/view.js +153 -0
  71. data/app/src/components/host/activity.js +111 -0
  72. data/app/src/components/host/details.js +28 -0
  73. data/app/src/components/host/executors.js +77 -0
  74. data/app/src/components/host/host_link.js +18 -0
  75. data/app/src/components/host/list.js +3 -0
  76. data/app/src/components/host/updaters.js +77 -0
  77. data/app/src/components/host/view.js +145 -0
  78. data/app/src/components/layer/list.js +3 -0
  79. data/app/src/components/layer/view.js +197 -0
  80. data/app/src/components/navbar/__tests__/navbar-test.js +21 -0
  81. data/app/src/components/navbar/nav_search_form.js +40 -0
  82. data/app/src/components/navbar/navbar.js +96 -0
  83. data/app/src/components/notfound/notfound.js +35 -0
  84. data/app/src/components/notfound/notfound.less +21 -0
  85. data/app/src/components/owned_resources/owned_resources.js +84 -0
  86. data/app/src/components/owned_resources/owned_resources_box.js +101 -0
  87. data/app/src/components/permissions/permissions.js +138 -0
  88. data/app/src/components/permissions/permissions_table.js +101 -0
  89. data/app/src/components/policy/list.js +3 -0
  90. data/app/src/components/policy/view.js +107 -0
  91. data/app/src/components/refresh/refresh.js +29 -0
  92. data/app/src/components/refresh/refresh.less +15 -0
  93. data/app/src/components/search/group.js +43 -0
  94. data/app/src/components/search/group_heading.js +50 -0
  95. data/app/src/components/search/group_title.js +37 -0
  96. data/app/src/components/search/result_item.js +55 -0
  97. data/app/src/components/search/search.js +118 -0
  98. data/app/src/components/user/activity.js +112 -0
  99. data/app/src/components/user/details.js +30 -0
  100. data/app/src/components/user/list.js +3 -0
  101. data/app/src/components/user/pubkeys.js +118 -0
  102. data/app/src/components/user/pubkeys.less +56 -0
  103. data/app/src/components/user/view.js +143 -0
  104. data/app/src/components/variable/activity.js +101 -0
  105. data/app/src/components/variable/details.js +46 -0
  106. data/app/src/components/variable/fetchers.js +77 -0
  107. data/app/src/components/variable/list.js +3 -0
  108. data/app/src/components/variable/updaters.js +77 -0
  109. data/app/src/components/variable/view.js +115 -0
  110. data/app/src/constants.js +36 -0
  111. data/{public → app/src}/images/conjur-logo.svg +0 -0
  112. data/{public → app/src}/images/icon-client-pc.svg +0 -0
  113. data/{public → app/src}/images/icon-environment.png +0 -0
  114. data/{public → app/src}/images/icon-person.svg +0 -0
  115. data/{public → app/src}/images/icon-policy.png +0 -0
  116. data/{public → app/src}/images/icon-resource.png +0 -0
  117. data/{public → app/src}/images/icon-service-dots.svg +0 -0
  118. data/{public → app/src}/images/icon-variable.png +0 -0
  119. data/app/src/pages/index.html +27 -0
  120. data/app/src/routes.js +64 -0
  121. data/app/src/stores/app_store.js +35 -0
  122. data/app/src/stores/audit_store.js +143 -0
  123. data/app/src/stores/graph_store.js +51 -0
  124. data/app/src/stores/group_store.js +104 -0
  125. data/app/src/stores/host_store.js +111 -0
  126. data/app/src/stores/layer_store.js +115 -0
  127. data/app/src/stores/policy_store.js +88 -0
  128. data/app/src/stores/resources_store.js +115 -0
  129. data/app/src/stores/route_store.js +21 -0
  130. data/app/src/stores/search_store.js +77 -0
  131. data/app/src/stores/user_store.js +109 -0
  132. data/app/src/stores/variable_store.js +93 -0
  133. data/app/src/styles/bootstrap.less +54 -0
  134. data/{public/css → app/src/styles}/styles.less +26 -82
  135. data/app/src/utils.js +38 -0
  136. data/app/src/vendor/pace.js +2 -0
  137. data/conjur-asset-ui.gemspec +3 -4
  138. data/docker/assets-build/Dockerfile +12 -0
  139. data/docker/conjur-ui/Dockerfile +33 -0
  140. data/docker/conjur-ui/README.md +38 -0
  141. data/docker/conjur-ui/mime.types +90 -0
  142. data/docker/conjur-ui/nginx.conf +110 -0
  143. data/docker/conjur-ui/start.py +72 -0
  144. data/docker/conjur-ui/start.sh +18 -0
  145. data/docker/conjur-ui/test.env +8 -0
  146. data/lib/conjur-asset-ui-version.rb +1 -1
  147. data/lib/conjur/command/ui.rb +10 -2
  148. data/lib/conjur/webserver/home.rb +3 -3
  149. data/lib/conjur/webserver/login.rb +1 -1
  150. data/lib/conjur/webserver/server.rb +16 -4
  151. data/public/js/views/roleGraph.js +91 -0
  152. metadata +167 -105
  153. data/.jshintrc +0 -41
  154. data/bower.json +0 -98
  155. data/gulpfile.js +0 -139
  156. data/package.json +0 -47
  157. data/preprocessor.js +0 -7
  158. data/public/_client_libs.html +0 -9
  159. data/public/index.html.erb +0 -63
  160. data/public/js/init.js +0 -196
  161. data/public/js/lib/pace.js +0 -2
  162. data/public/js/lib/sorted-set.no-require.js +0 -1145
  163. data/public/js/lib/sorted-set.no-require.js.txt +0 -6
  164. data/public/js/models/groupRecord.js +0 -72
  165. data/public/js/models/hostRecord.js +0 -60
  166. data/public/js/models/layerRecord.js +0 -79
  167. data/public/js/models/namespace.js +0 -12
  168. data/public/js/models/policyList.js +0 -16
  169. data/public/js/models/policyRecord.js +0 -54
  170. data/public/js/models/record.js +0 -117
  171. data/public/js/models/resourceList.js +0 -87
  172. data/public/js/models/userList.js +0 -25
  173. data/public/js/models/userRecord.js +0 -75
  174. data/public/js/models/variableList.js +0 -27
  175. data/public/js/models/variableRecord.js +0 -77
  176. data/public/js/routers.js +0 -242
  177. data/public/js/views/annotations.js +0 -47
  178. data/public/js/views/audit.js +0 -369
  179. data/public/js/views/breadcrumbs.js +0 -62
  180. data/public/js/views/chart.js +0 -617
  181. data/public/js/views/dashboard.js +0 -146
  182. data/public/js/views/generic.js +0 -122
  183. data/public/js/views/group.js +0 -109
  184. data/public/js/views/groups.js +0 -26
  185. data/public/js/views/host.js +0 -200
  186. data/public/js/views/hosts.js +0 -26
  187. data/public/js/views/layer.js +0 -146
  188. data/public/js/views/layers.js +0 -26
  189. data/public/js/views/mixins/search.js +0 -22
  190. data/public/js/views/mixins/tabs.js +0 -154
  191. data/public/js/views/namespaces.js +0 -40
  192. data/public/js/views/navSearch.js +0 -36
  193. data/public/js/views/owned.js +0 -184
  194. data/public/js/views/permissions.js +0 -254
  195. data/public/js/views/policies.js +0 -26
  196. data/public/js/views/policy.js +0 -70
  197. data/public/js/views/resource.js +0 -59
  198. data/public/js/views/role.js +0 -63
  199. data/public/js/views/searchResults.js +0 -212
  200. data/public/js/views/sections.js +0 -226
  201. data/public/js/views/time.js +0 -39
  202. data/public/js/views/user.js +0 -297
  203. data/public/js/views/users.js +0 -26
  204. data/public/js/views/variable.js +0 -310
  205. data/public/js/views/variables.js +0 -26
  206. data/spec/javascripts/helpers/.gitkeep +0 -0
  207. data/spec/javascripts/support/jasmine.yml +0 -112
  208. data/spec/javascripts/support/jasmine_helper.rb +0 -22
  209. data/spec/javascripts/support/run.html.erb +0 -23
  210. data/spec/javascripts/views/AuditSpec.js +0 -22
  211. data/spec/javascripts/views/AuditSpec.ls +0 -18
@@ -0,0 +1,48 @@
1
+ /* global jest, describe, it, expect */
2
+ "use strict";
3
+
4
+
5
+ jest.dontMock('lodash');
6
+ jest.dontMock('collections/set');
7
+ jest.dontMock('../helpers');
8
+
9
+ var {edgesFromVertices, __test__:{SingleVertex}} = require('../helpers');
10
+ var Set = require('collections/set');
11
+
12
+ describe('edgesFromVertices', ()=>{
13
+
14
+ var a = new SingleVertex('x:a'),
15
+ b = new SingleVertex('x:b'),
16
+ c = new SingleVertex('x:c'),
17
+ d = new SingleVertex('x:d');
18
+ a.addChild(b);
19
+ a.addChild(c);
20
+ b.addChild(d);
21
+ c.addChild(d);
22
+ var vertices = [a,b,c,d];
23
+
24
+ var edges = edgesFromVertices(vertices),
25
+ edgeSet = new Set(
26
+ edges,
27
+ (a,b) => a.parent === b.parent && a.child === b.child,
28
+ (a) => `${a.parent}->${a.child}`
29
+ ) ;
30
+
31
+
32
+ function expectEdge(parent, child){
33
+ expect(edgeSet.has({parent, child})).toBeTruthy();
34
+ }
35
+
36
+ it('has the expected edges', ()=>{
37
+ expectEdge('x:a', 'x:b');
38
+ expectEdge('x:a', 'x:c');
39
+ expectEdge('x:b', 'x:d');
40
+ expectEdge('x:c', 'x:d');
41
+ });
42
+
43
+ it('has the expected number of edges', ()=>{
44
+ expect(edges.length).toBe(4);
45
+ })
46
+
47
+
48
+ });
@@ -0,0 +1,16 @@
1
+ /* global jest, describe, it, expect */
2
+ "use strict";
3
+ jest.dontMock('../helpers');
4
+ jest.dontMock('collections/set');
5
+ var {newVertexSet} = require('../helpers').__test__;
6
+
7
+
8
+ describe('newVertexSet', ()=>{
9
+ it('creates as set that is keyed by id', ()=>{
10
+ const s = newVertexSet();
11
+ s.add({id:'foo'});
12
+ s.add({id:'bar'});
13
+ s.add({id:'foo'});
14
+ expect(s.length).toBe(2);
15
+ });
16
+ });
@@ -0,0 +1,27 @@
1
+ /* global jest, describe, it, expect */
2
+ "use strict";
3
+
4
+ jest.dontMock('collections/set');
5
+ jest.dontMock('../helpers');
6
+
7
+ var {nextId} = require('../helpers').__test__;
8
+ var Set = require('collections/set');
9
+
10
+ describe('nextId', ()=> {
11
+ it('generates unique ids', ()=> {
12
+ var s = new Set();
13
+ for (let i = 0; i < 100; i++) {
14
+ s.add(nextId());
15
+ }
16
+ expect(s.length).toBe(100);
17
+ });
18
+
19
+ it('returns strings when no prefix is given', ()=>{
20
+ expect(typeof nextId()).toBe('string');
21
+ });
22
+
23
+ it('uses a prefix if given', ()=>{
24
+ expect(nextId('hello').indexOf('hello')).toBe(0);
25
+ });
26
+
27
+ });
@@ -0,0 +1,24 @@
1
+ /* global jest, describe, it, expect */
2
+ "use strict";
3
+
4
+ jest.dontMock('../helpers');
5
+
6
+ var {roleKindFromId} = require('../helpers').__test__;
7
+
8
+ describe('roleKindFromId', () =>{
9
+ it('parses an id with 3 components correctly', () =>{
10
+ expect(roleKindFromId('companyname:user:bob')).toBe('user');
11
+ });
12
+
13
+ it('parses an id with more than three components correctly', ()=>{
14
+ expect(roleKindFromId('a:b:c:d')).toBe('b');
15
+ });
16
+
17
+ it('parses an id with 2 components correctly', ()=>{
18
+ expect(roleKindFromId('user:bob')).toBe('user');
19
+ });
20
+
21
+ it('returns the id unchanged when it has a single component', () =>{
22
+ expect(roleKindFromId('asdf')).toBe('asdf');
23
+ });
24
+ });
@@ -0,0 +1,72 @@
1
+ /* global jest, describe, it, expect */
2
+ "use strict";
3
+
4
+
5
+ jest.dontMock('lodash');
6
+ jest.dontMock('collections/set');
7
+ jest.dontMock('../helpers');
8
+
9
+ var {verticesFromEdges} = require('../helpers');
10
+ var {reduce} = require('lodash');
11
+
12
+
13
+ describe('verticesFromEdges', ()=> {
14
+ /*
15
+ x:c
16
+ |
17
+ v
18
+ x:a
19
+ / |
20
+ x:b x:d
21
+ \ /
22
+ x:e
23
+ */
24
+ var edges = [
25
+ ['x:a', 'x:b'],
26
+ ['x:c', 'x:a'],
27
+ ['x:a', 'x:d'],
28
+ ['x:d', 'x:e'],
29
+ ['x:b', 'x:e']
30
+ ];
31
+
32
+ var vertices = verticesFromEdges(edges);
33
+
34
+
35
+ var idToVertex = reduce(vertices,
36
+ (o, v) => {
37
+ o[v.id] = v;
38
+ return o;
39
+ },
40
+ {}
41
+ );
42
+
43
+ function expectStructure(vertexId, expectedInIds, expectedOutIds){
44
+ var vertex = idToVertex[vertexId];
45
+ expect(vertex.in.length).toBe(expectedInIds.length);
46
+ expectedInIds.forEach(
47
+ (vId) => {
48
+ let v = idToVertex[vId];
49
+ expect(vertex.in.has(v)).toBeTruthy();
50
+ }
51
+ );
52
+ expectedOutIds.forEach(
53
+ (vId) => {
54
+ let v = idToVertex[vId];
55
+ expect(vertex.out.has(v)).toBeTruthy();
56
+ }
57
+ );
58
+ }
59
+
60
+ it('makes 5 vertices', ()=> {
61
+ expect(vertices.length).toBe(5);
62
+ });
63
+
64
+ it('creates the correct structure', () => {
65
+ expectStructure('x:c', [], ['x:a']);
66
+ expectStructure('x:a', ['x:c'], ['x:b', 'x:d']);
67
+ expectStructure('x:b', ['x:a'], ['x:e']);
68
+ expectStructure('x:d', ['x:a'], ['x:e']);
69
+ expectStructure('x:e', ['x:b', 'x:d'], []);
70
+ });
71
+
72
+ });
@@ -0,0 +1,449 @@
1
+ "use strict";
2
+
3
+ require('./graph.less');
4
+
5
+ var React = require('react'),
6
+ Router = require('react-router'),
7
+ Fluxxor = require('fluxxor'),
8
+ FluxMixin = Fluxxor.FluxMixin(React),
9
+ StoreWatchMixin = Fluxxor.StoreWatchMixin,
10
+ dagre = require('dagre-d3'),
11
+ d3 = require('d3');
12
+
13
+ import {forEach, flatten, unique, chain, merge} from 'lodash';
14
+ import {verticesFromEdges, isInternal, collapse,
15
+ edgesFromVertices, newEdgeSet} from './helpers';
16
+ var Set = require('collections/set');
17
+
18
+ var RoleLink = require('../generic/role_link');
19
+
20
+ function Node(type, props) {
21
+ merge(this, props, {type: type});
22
+ }
23
+
24
+ // helpers
25
+ function _translate(x,y) {
26
+ return 'translate(' + x + ',' + y + ')';
27
+ }
28
+
29
+ function _findParent(elt, predicate) {
30
+ while (elt && !predicate(elt)) {
31
+ elt = elt.parentElement;
32
+ }
33
+
34
+ if (!elt) {
35
+ throw new Error('couldn\'t find parent');
36
+ }
37
+
38
+ return elt;
39
+ }
40
+
41
+
42
+
43
+ /**
44
+ * Displays a digraph representing a role tree. Usage:
45
+ *
46
+ * <RoleGraph margin={...} ref='roleGraph'/>
47
+ *
48
+ * ...
49
+ *
50
+ * // show a graph, which should be an array of {parent, child} objects.
51
+ * this.refs.roleGraph.setState({graph: res.graph})
52
+ */
53
+ var RoleGraph = React.createClass({
54
+ mixins: [Router.State, Router.Navigation],
55
+
56
+ /*
57
+ * React Lifecycle methods
58
+ */
59
+ render() {
60
+ return (
61
+ <div className="role-graph-container"></div>
62
+ );
63
+ },
64
+
65
+ getDefaultProps() {
66
+ return {
67
+ defaultOptions: {
68
+ margin: {
69
+ top: 20,
70
+ right: 20,
71
+ bottom: 20,
72
+ left: 20
73
+ }
74
+ }
75
+ };
76
+ },
77
+
78
+ getInitialState() {
79
+ return {
80
+ ready: false
81
+ };
82
+ },
83
+
84
+ componentDidMount() {
85
+ this.ctx = {};
86
+ this.el = this.getDOMNode();
87
+ this._propsToState(this.props);
88
+ },
89
+
90
+ componentWillReceiveProps(nextProps) {
91
+ this._propsToState(nextProps);
92
+ },
93
+
94
+ componentDidUpdate() {
95
+ if (this.state.ready === false) {
96
+ return;
97
+ }
98
+
99
+ if (this.ctx.svg === undefined) {
100
+ this._create();
101
+ }
102
+
103
+ if (this.state.graph !== undefined) {
104
+ this._update();
105
+ }
106
+ },
107
+
108
+ /**
109
+ * Internal methods
110
+ */
111
+
112
+ /**
113
+ * Create non-data bound components. May be called whenever state.ready is true
114
+ * @private
115
+ */
116
+ _create() {
117
+ this.ctx.svg = d3.select(this.el)
118
+ .append('svg')
119
+ .attr('class', 'role-graph-svg');
120
+ this.ctx.inner = this.ctx.svg.append('g');
121
+ },
122
+
123
+ /**
124
+ * Render the graph represented by this.state.graph. This may be
125
+ * called whenever this.state.graph is defined. this.state.graph
126
+ * must be an array of {child:'child-id', parent:'parent-id'}
127
+ * objects.
128
+ *
129
+ * @private
130
+ */
131
+ _update() {
132
+ var svg = this.ctx.svg;
133
+ var graph = this._prepareD3Graph();
134
+ var inner = this.ctx.inner;
135
+ var render = new dagre.render();
136
+ render(inner, graph);
137
+
138
+ // Move and resize the SVG to match the width and height of
139
+ // the graph. Here, we use this.state.margin to compute the
140
+ // position of the <g/> element containing the graph within the
141
+ // root <svg> element, and the size of both the container and
142
+ // the svg.
143
+
144
+ var margin = this.state.margin;
145
+ var gWidth = graph.graph().width,
146
+ gHeight = graph.graph().height;
147
+
148
+ // total width/height are gWidth/gHeight + margin
149
+ var totalHeight = gHeight + margin.top + margin.bottom;
150
+ var totalWidth = gWidth + margin.left + margin.right;
151
+
152
+ var svgSize = this._computeSvgSize(gWidth, gHeight);
153
+
154
+ // resize the svg
155
+ svg.attr('height', svgSize.height)
156
+ .attr('width', svgSize.width);
157
+
158
+ // resize the inner <g> and translate it to the top left point of the
159
+ // margin.
160
+ inner.attr('height', gHeight)
161
+ .attr('width', gWidth)
162
+ .attr('transform',_translate(margin.left, margin.top));
163
+
164
+ // Set up zoom support
165
+ var zoom = d3.behavior.zoom().on('zoom', function() {
166
+ inner.attr('transform', 'translate(' + d3.event.translate + ')' +
167
+ 'scale(' + d3.event.scale + ')');
168
+ });
169
+
170
+ // scale to initial scale
171
+ zoom.scale(svgSize.scale)
172
+ .translate([(svgSize.width - gWidth * svgSize.scale) / 2, (svgSize.height - gHeight * svgSize.scale) / 2]) // center it
173
+ .event(svg);
174
+ svg.call(zoom);
175
+ },
176
+
177
+ /**
178
+ * Return width and height attributes that will fit in our element (or fit the
179
+ * graph if our element isn't specifying a width and height), and a scale
180
+ * to apply to the graph to fit it in the svg, based on the given graph
181
+ * width and height.
182
+ * @param graphWidth Width, in pixels, of the graph
183
+ * @param graphHeight Height, in pixels, of the graph
184
+ * @return {{width:number, height: number, scale: number}}
185
+ *
186
+ * @private
187
+ */
188
+
189
+ _computeSvgSize(graphWidth, graphHeight) {
190
+ var eltWidth = +this.el.offsetWidth,
191
+ eltHeight = +this.el.offsetHeight,
192
+ width = graphWidth,
193
+ height = graphHeight;
194
+
195
+ if (!isNaN(eltWidth) && eltWidth !== 0) {
196
+ width = Math.min(graphWidth, eltWidth);
197
+ }
198
+
199
+ if (!isNaN(eltHeight) && eltHeight !== 0) {
200
+ height = Math.min(graphHeight, eltHeight);
201
+ }
202
+
203
+ if (this.props.width) {
204
+ width = Math.max(this.props.width, width);
205
+ }
206
+
207
+ if (this.props.height) {
208
+ height = Math.max(this.props.height, height);
209
+ }
210
+
211
+ var scale = Math.min(width, height) / Math.max(graphWidth, graphHeight);
212
+
213
+ if (scale > 1) {
214
+ scale *= .9; // leave some room at the edges when the graph is smaller than the element.
215
+ }
216
+
217
+ return {width: width, height: height, scale: scale};
218
+ },
219
+
220
+ // creates an HTML role-link for the role.
221
+ _createRoleLabel(roleid) {
222
+ return React.withContext(this.context, function() {
223
+ var roleLink = <RoleLink id={roleid} simple={true} />;
224
+ return React.renderToString(roleLink);
225
+ });
226
+ },
227
+
228
+ /**
229
+ * Build a graphlib Graph object from the graph data returned by
230
+ * the conjur api.
231
+ *
232
+ * TODO this method is getting kinda big!
233
+ *
234
+ * May be called whenever this.state.graph is defined.
235
+ * @returns {degreD3.graphlib.Graph}
236
+ * @private
237
+ */
238
+ _prepareD3Graph() {
239
+ // throw something helpful if we're called in a bad state.
240
+ if(!this.state.graph){
241
+ throw new Error("_prepareD3Graph called without state.graph");
242
+ }
243
+
244
+ var {graph, invertGraph, roleid} = this.state;
245
+ const invert = invertGraph === undefined ? true : invertGraph;
246
+
247
+ var vertices = verticesFromEdges(graph, invert);
248
+
249
+
250
+
251
+ // remove internal roles from the graph,
252
+ // unless the role being shown is internal (presently this is
253
+ // impossible in the UI, but we might as well check in case this changes.
254
+ if (!isInternal(roleid)) {
255
+ vertices.forEach((v) => {
256
+ if(isInternal(v.id)) v.unlink();
257
+ });
258
+ vertices = _.filter(vertices, (v) => v.isConnected());
259
+ }
260
+
261
+
262
+ // Collapse big sets of equivalent vertices.
263
+ vertices = collapse(vertices,
264
+ this.props.collapseCutoff);
265
+
266
+
267
+ // graph properties go here
268
+ var dagreGraph = this.graph = new dagre.graphlib.Graph().setGraph({});
269
+
270
+ // keep track of edges we've added
271
+ var edgeSet = newEdgeSet();
272
+
273
+ // and vertex ids
274
+ var vertexIdSet = new Set();
275
+
276
+ // add an edge idempotently
277
+ var addEdge = (parent, child) => {
278
+ if(edgeSet.add({parent, child})){
279
+ dagreGraph.setEdge(parent.id, child.id,
280
+ this._makeEdgeProps(parent, child));
281
+ }
282
+ };
283
+
284
+ // add a vertex idempotently
285
+ var addVertex = (v)=> {
286
+ if(vertexIdSet.add(v.id)){
287
+ dagreGraph.setNode(v.id, this._makeNodeProps(v));
288
+ v.in.forEach((p) => addEdge(p, v));
289
+ v.out.forEach((c) => addEdge(v, c));
290
+ }
291
+ };
292
+
293
+ vertices.forEach(addVertex);
294
+
295
+
296
+ var selfNode = dagreGraph.node(this.state.roleid);
297
+
298
+ if (selfNode === undefined) {
299
+ console.error("current role isn't in graph?");
300
+ }else{
301
+ selfNode['class'] = 'current-role';
302
+ }
303
+
304
+ return dagreGraph;
305
+ },
306
+
307
+ _makeNodeProps(vertex){
308
+ if(vertex.isSingle()){
309
+ return this._makeSingleNodeProps(vertex);
310
+ }else{
311
+ return this._makeMultiNodeProps(vertex);
312
+ }
313
+ },
314
+
315
+ _makeSingleNodeProps(vertex){
316
+ var html = React.withContext(this.context,
317
+ ()=> React.renderToString(<RoleLink id={vertex.id}/>));
318
+ return {label: html, labelType: 'html'};
319
+ },
320
+
321
+ _makeMultiNodeProps(vertex){
322
+ const count = vertex.members.length,
323
+ roleKind = vertex.roleKind,
324
+ label = `${count} identical ${roleKind}s`;
325
+ return {label};
326
+ },
327
+
328
+ _makeEdgeProps(fromVertex, toVertex){
329
+ return {}; // Add fanciness for edges here.
330
+ },
331
+
332
+ /**
333
+ * Transfer some props to our state, and determine whether we're ready to
334
+ * call _create.
335
+ * @param props contains a 'margin' object.
336
+ * @private
337
+ */
338
+ _propsToState(props) {
339
+ var margin = props.margin ? props.margin : this.props.defaultOptions.margin;
340
+
341
+ var state = {
342
+ ready: margin !== undefined && this.props.roleid !== undefined,
343
+ margin: margin,
344
+ invertGraph: this.props.invertGraph,
345
+ roleid: this.props.roleid
346
+ };
347
+
348
+ if (props.graph !== undefined) {
349
+ state.graph = props.graph;
350
+ }
351
+
352
+ this.setState(state);
353
+ }
354
+
355
+
356
+ });
357
+
358
+ export default React.createClass({
359
+ displayName: 'RoleGraph',
360
+
361
+ mixins: [FluxMixin, StoreWatchMixin('graph')],
362
+
363
+ getStateFromFlux() {
364
+ var flux = this.getFlux(),
365
+ graph = flux.store('graph').getGraph(this.props.kind, this.props.id);
366
+
367
+ return graph;
368
+ },
369
+
370
+ render() {
371
+ if (this.state.error) {
372
+ return null;
373
+ }
374
+
375
+ if (this.state.loading) {
376
+ return (
377
+ <div className="rg-container">
378
+ <h2>Role Graph</h2>
379
+ Loading
380
+ </div>
381
+ );
382
+ }
383
+
384
+ if (this.state.data.length === 0) {
385
+ return (
386
+ <div className="rg-container">
387
+ <h2>Role Graph</h2>
388
+ No data exists
389
+ </div>
390
+ );
391
+ }
392
+
393
+ return this._renderGraph();
394
+ },
395
+
396
+ componentDidMount() {
397
+ var actions = this.getFlux().actions;
398
+
399
+ actions.graph.load(this.props.kind, this.props.id);
400
+ },
401
+
402
+ _renderGraph() {
403
+ var size = this._computeGraphSize(),
404
+ invert = this.props.invertGraph === undefined ? true : this.props.invertGraph,
405
+ roleid = window.decodeURIComponent(this.getFlux().actions.getFullId(this.props.kind, this.props.id));
406
+
407
+ return (
408
+ <div className="rg-container loaded">
409
+ <h2>Role Graph</h2>
410
+ <RoleGraph graph={this.state.data}
411
+ width={size.width}
412
+ height={size.height}
413
+ roleid={roleid}
414
+ invertGraph={invert} />
415
+ </div>
416
+ );
417
+ },
418
+
419
+ _computeGraphSize() {
420
+ var el = this.getDOMNode(),
421
+ width = this.props.width,
422
+ height = this.props.height;
423
+
424
+ if (isNaN(width)) {
425
+ width = +el.offsetWidth;
426
+ }
427
+
428
+ if (isNaN(height)) {
429
+ height = +el.offsetHeight;
430
+ }
431
+
432
+ if (isNaN(width) || isNaN(height)) {
433
+ throw new Error('can\'t determine width and height. ' +
434
+ 'Perhaps you should set width and height props on your RoleGraphContainer.');
435
+ }
436
+
437
+ // we can set height ourselves but width should be computed from the parent.
438
+ if (width == 0) {
439
+ width = _findParent(el, function(e) {
440
+ return e.offsetWidth !== 0;
441
+ }).offsetWidth; // _findParent is a partial function, if no such parent exists it will throw an Error.
442
+ }
443
+
444
+ return {
445
+ width: width,
446
+ height: height
447
+ };
448
+ }
449
+ });