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.
- checksums.yaml +4 -4
- data/.gitignore +8 -3
- data/CHANGELOG.md +10 -0
- data/Makefile +19 -0
- data/README.md +0 -3
- data/Rakefile +22 -17
- data/TODO.md +0 -23
- data/app/.csscomb.json +304 -0
- data/app/.eslintignore +3 -0
- data/app/.eslintrc +265 -0
- data/app/config/preprocessor.js +19 -0
- data/app/config/webpack.js +124 -0
- data/app/gulpfile.js +96 -0
- data/app/package.json +86 -0
- data/app/src/actions.js +550 -0
- data/app/src/app.js +83 -0
- data/app/src/clients/audit.js +34 -0
- data/app/src/clients/auth.js +24 -0
- data/app/src/clients/generic.js +52 -0
- data/app/src/clients/graph.js +7 -0
- data/app/src/clients/layer_members.js +18 -0
- data/app/src/clients/list.js +31 -0
- data/app/src/clients/members.js +20 -0
- data/app/src/clients/request.js +531 -0
- data/app/src/clients/search.js +5 -0
- data/app/src/components/app/__tests__/app-test.js +22 -0
- data/app/src/components/app/app.js +36 -0
- data/app/src/components/app/wrapper.js +17 -0
- data/app/src/components/audit/__tests__/table_header-test.js +22 -0
- data/app/src/components/audit/box.js +9 -0
- data/app/src/components/audit/constants.js +5 -0
- data/app/src/components/audit/entry.js +105 -0
- data/app/src/components/audit/fields_mixin.js +11 -0
- data/app/src/components/audit/humanize_event.js +213 -0
- data/app/src/components/audit/table.js +64 -0
- data/app/src/components/audit/table_header.js +37 -0
- data/app/src/components/audit/timestamp.js +28 -0
- data/app/src/components/auth/login.js +177 -0
- data/app/src/components/auth/login.less +71 -0
- data/app/src/components/auth/logout.js +42 -0
- data/app/src/components/auth/logout.less +21 -0
- data/app/src/components/chart/chart.js +540 -0
- data/app/src/components/chart/chart_helper_mixin.js +78 -0
- data/app/src/components/custom/list.js +3 -0
- data/app/src/components/custom/view.js +81 -0
- data/app/src/components/dashboard/activity.js +144 -0
- data/app/src/components/dashboard/dashboard.js +46 -0
- data/app/src/components/flash/flash.js +98 -0
- data/app/src/components/flash/flash.less +3 -0
- data/app/src/components/generic/__tests__/time-test.js +42 -0
- data/app/src/components/generic/annotations.js +39 -0
- data/app/src/components/generic/breadcrumbs.js +57 -0
- data/app/src/components/generic/foldable_audit_section.js +204 -0
- data/app/src/components/generic/list.js +141 -0
- data/app/src/components/generic/list_factory.js +41 -0
- data/app/src/components/generic/resource_link.js +64 -0
- data/app/src/components/generic/role_link.js +66 -0
- data/app/src/components/generic/tab_mixin.js +146 -0
- data/app/src/components/generic/time.js +32 -0
- data/app/src/components/graph/__tests__/collapse-test.js +133 -0
- data/app/src/components/graph/__tests__/edges-from-vertices-test.js +48 -0
- data/app/src/components/graph/__tests__/new-vertex-set-test.js +16 -0
- data/app/src/components/graph/__tests__/next-id-test.js +27 -0
- data/app/src/components/graph/__tests__/role-kind-from-id-test.js +24 -0
- data/app/src/components/graph/__tests__/vertices-from-edges-test.js +72 -0
- data/app/src/components/graph/graph.js +449 -0
- data/app/src/components/graph/graph.less +39 -0
- data/app/src/components/graph/helpers.js +368 -0
- data/app/src/components/group/list.js +3 -0
- data/app/src/components/group/view.js +153 -0
- data/app/src/components/host/activity.js +111 -0
- data/app/src/components/host/details.js +28 -0
- data/app/src/components/host/executors.js +77 -0
- data/app/src/components/host/host_link.js +18 -0
- data/app/src/components/host/list.js +3 -0
- data/app/src/components/host/updaters.js +77 -0
- data/app/src/components/host/view.js +145 -0
- data/app/src/components/layer/list.js +3 -0
- data/app/src/components/layer/view.js +197 -0
- data/app/src/components/navbar/__tests__/navbar-test.js +21 -0
- data/app/src/components/navbar/nav_search_form.js +40 -0
- data/app/src/components/navbar/navbar.js +96 -0
- data/app/src/components/notfound/notfound.js +35 -0
- data/app/src/components/notfound/notfound.less +21 -0
- data/app/src/components/owned_resources/owned_resources.js +84 -0
- data/app/src/components/owned_resources/owned_resources_box.js +101 -0
- data/app/src/components/permissions/permissions.js +138 -0
- data/app/src/components/permissions/permissions_table.js +101 -0
- data/app/src/components/policy/list.js +3 -0
- data/app/src/components/policy/view.js +107 -0
- data/app/src/components/refresh/refresh.js +29 -0
- data/app/src/components/refresh/refresh.less +15 -0
- data/app/src/components/search/group.js +43 -0
- data/app/src/components/search/group_heading.js +50 -0
- data/app/src/components/search/group_title.js +37 -0
- data/app/src/components/search/result_item.js +55 -0
- data/app/src/components/search/search.js +118 -0
- data/app/src/components/user/activity.js +112 -0
- data/app/src/components/user/details.js +30 -0
- data/app/src/components/user/list.js +3 -0
- data/app/src/components/user/pubkeys.js +118 -0
- data/app/src/components/user/pubkeys.less +56 -0
- data/app/src/components/user/view.js +143 -0
- data/app/src/components/variable/activity.js +101 -0
- data/app/src/components/variable/details.js +46 -0
- data/app/src/components/variable/fetchers.js +77 -0
- data/app/src/components/variable/list.js +3 -0
- data/app/src/components/variable/updaters.js +77 -0
- data/app/src/components/variable/view.js +115 -0
- data/app/src/constants.js +36 -0
- data/{public → app/src}/images/conjur-logo.svg +0 -0
- data/{public → app/src}/images/icon-client-pc.svg +0 -0
- data/{public → app/src}/images/icon-environment.png +0 -0
- data/{public → app/src}/images/icon-person.svg +0 -0
- data/{public → app/src}/images/icon-policy.png +0 -0
- data/{public → app/src}/images/icon-resource.png +0 -0
- data/{public → app/src}/images/icon-service-dots.svg +0 -0
- data/{public → app/src}/images/icon-variable.png +0 -0
- data/app/src/pages/index.html +27 -0
- data/app/src/routes.js +64 -0
- data/app/src/stores/app_store.js +35 -0
- data/app/src/stores/audit_store.js +143 -0
- data/app/src/stores/graph_store.js +51 -0
- data/app/src/stores/group_store.js +104 -0
- data/app/src/stores/host_store.js +111 -0
- data/app/src/stores/layer_store.js +115 -0
- data/app/src/stores/policy_store.js +88 -0
- data/app/src/stores/resources_store.js +115 -0
- data/app/src/stores/route_store.js +21 -0
- data/app/src/stores/search_store.js +77 -0
- data/app/src/stores/user_store.js +109 -0
- data/app/src/stores/variable_store.js +93 -0
- data/app/src/styles/bootstrap.less +54 -0
- data/{public/css → app/src/styles}/styles.less +26 -82
- data/app/src/utils.js +38 -0
- data/app/src/vendor/pace.js +2 -0
- data/conjur-asset-ui.gemspec +3 -4
- data/docker/assets-build/Dockerfile +12 -0
- data/docker/conjur-ui/Dockerfile +33 -0
- data/docker/conjur-ui/README.md +38 -0
- data/docker/conjur-ui/mime.types +90 -0
- data/docker/conjur-ui/nginx.conf +110 -0
- data/docker/conjur-ui/start.py +72 -0
- data/docker/conjur-ui/start.sh +18 -0
- data/docker/conjur-ui/test.env +8 -0
- data/lib/conjur-asset-ui-version.rb +1 -1
- data/lib/conjur/command/ui.rb +10 -2
- data/lib/conjur/webserver/home.rb +3 -3
- data/lib/conjur/webserver/login.rb +1 -1
- data/lib/conjur/webserver/server.rb +16 -4
- data/public/js/views/roleGraph.js +91 -0
- metadata +167 -105
- data/.jshintrc +0 -41
- data/bower.json +0 -98
- data/gulpfile.js +0 -139
- data/package.json +0 -47
- data/preprocessor.js +0 -7
- data/public/_client_libs.html +0 -9
- data/public/index.html.erb +0 -63
- data/public/js/init.js +0 -196
- data/public/js/lib/pace.js +0 -2
- data/public/js/lib/sorted-set.no-require.js +0 -1145
- data/public/js/lib/sorted-set.no-require.js.txt +0 -6
- data/public/js/models/groupRecord.js +0 -72
- data/public/js/models/hostRecord.js +0 -60
- data/public/js/models/layerRecord.js +0 -79
- data/public/js/models/namespace.js +0 -12
- data/public/js/models/policyList.js +0 -16
- data/public/js/models/policyRecord.js +0 -54
- data/public/js/models/record.js +0 -117
- data/public/js/models/resourceList.js +0 -87
- data/public/js/models/userList.js +0 -25
- data/public/js/models/userRecord.js +0 -75
- data/public/js/models/variableList.js +0 -27
- data/public/js/models/variableRecord.js +0 -77
- data/public/js/routers.js +0 -242
- data/public/js/views/annotations.js +0 -47
- data/public/js/views/audit.js +0 -369
- data/public/js/views/breadcrumbs.js +0 -62
- data/public/js/views/chart.js +0 -617
- data/public/js/views/dashboard.js +0 -146
- data/public/js/views/generic.js +0 -122
- data/public/js/views/group.js +0 -109
- data/public/js/views/groups.js +0 -26
- data/public/js/views/host.js +0 -200
- data/public/js/views/hosts.js +0 -26
- data/public/js/views/layer.js +0 -146
- data/public/js/views/layers.js +0 -26
- data/public/js/views/mixins/search.js +0 -22
- data/public/js/views/mixins/tabs.js +0 -154
- data/public/js/views/namespaces.js +0 -40
- data/public/js/views/navSearch.js +0 -36
- data/public/js/views/owned.js +0 -184
- data/public/js/views/permissions.js +0 -254
- data/public/js/views/policies.js +0 -26
- data/public/js/views/policy.js +0 -70
- data/public/js/views/resource.js +0 -59
- data/public/js/views/role.js +0 -63
- data/public/js/views/searchResults.js +0 -212
- data/public/js/views/sections.js +0 -226
- data/public/js/views/time.js +0 -39
- data/public/js/views/user.js +0 -297
- data/public/js/views/users.js +0 -26
- data/public/js/views/variable.js +0 -310
- data/public/js/views/variables.js +0 -26
- data/spec/javascripts/helpers/.gitkeep +0 -0
- data/spec/javascripts/support/jasmine.yml +0 -112
- data/spec/javascripts/support/jasmine_helper.rb +0 -22
- data/spec/javascripts/support/run.html.erb +0 -23
- data/spec/javascripts/views/AuditSpec.js +0 -22
- data/spec/javascripts/views/AuditSpec.ls +0 -18
@@ -0,0 +1,39 @@
|
|
1
|
+
// MUST BE done in BEM style!!
|
2
|
+
|
3
|
+
.role-graph {
|
4
|
+
height: 300px;
|
5
|
+
}
|
6
|
+
|
7
|
+
.role-graph-svg {
|
8
|
+
border: 1px solid #CCC;
|
9
|
+
}
|
10
|
+
|
11
|
+
.rg-container {
|
12
|
+
width: 100%;
|
13
|
+
}
|
14
|
+
|
15
|
+
.rg-container .loaded {
|
16
|
+
min-height: 500px;
|
17
|
+
}
|
18
|
+
|
19
|
+
/** SVG role graph styles **/
|
20
|
+
.node rect {
|
21
|
+
stroke: #333;
|
22
|
+
fill: #fff;
|
23
|
+
}
|
24
|
+
|
25
|
+
svg .label {
|
26
|
+
font-weight: normal;
|
27
|
+
height: auto;
|
28
|
+
width: auto;
|
29
|
+
}
|
30
|
+
|
31
|
+
.edgePath path {
|
32
|
+
stroke: #333;
|
33
|
+
fill: #333;
|
34
|
+
stroke-width: 1.5px;
|
35
|
+
}
|
36
|
+
|
37
|
+
g.current-role > rect {
|
38
|
+
fill: #7F7;
|
39
|
+
}
|
@@ -0,0 +1,368 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
var _ = require('lodash');
|
4
|
+
import Set from 'collections/set';
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Returns the 'kind' part of a Conjur id like
|
8
|
+
* "kind:id" or "account:kind:id". Returns the id unchanged
|
9
|
+
* when it isn't in this format (useful for tests and such).
|
10
|
+
* @param id {String}
|
11
|
+
* @returns {String}
|
12
|
+
*/
|
13
|
+
function roleKindFromId(id) {
|
14
|
+
let parts = id.split(':', 3);
|
15
|
+
if (parts.length === 2) {
|
16
|
+
return parts[0];
|
17
|
+
} else if (parts.length === 3) {
|
18
|
+
return parts[1];
|
19
|
+
} else {
|
20
|
+
return id;
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
/**
|
25
|
+
* Create a new collection capable of storing vertices.
|
26
|
+
*/
|
27
|
+
function newVertexSet() {
|
28
|
+
return new Set(
|
29
|
+
null, // values
|
30
|
+
(a, b) => a.id === b.id, // equals
|
31
|
+
(a) => a.id // hash
|
32
|
+
);
|
33
|
+
}
|
34
|
+
|
35
|
+
/**
|
36
|
+
* Return a new id with the given prefix
|
37
|
+
*/
|
38
|
+
var nextId = (function () {
|
39
|
+
var id = 1;
|
40
|
+
return function (prefix = '') {
|
41
|
+
return prefix + (id++);
|
42
|
+
}
|
43
|
+
})();
|
44
|
+
|
45
|
+
/**
|
46
|
+
* Base class for vertices of an adjacency list representation
|
47
|
+
* of a graph.
|
48
|
+
*/
|
49
|
+
class BaseVertex {
|
50
|
+
constructor(id) {
|
51
|
+
this.id = id;
|
52
|
+
this.roleKind = roleKindFromId(id);
|
53
|
+
this.in = newVertexSet();
|
54
|
+
this.out = newVertexSet();
|
55
|
+
}
|
56
|
+
|
57
|
+
// Poor mans polymorphism -- it'd be cool to have a toNodeAttributes here,
|
58
|
+
// but that seems too highly coupled to the UI code. As it stands, the UI code
|
59
|
+
// decides how to create node attributes by checking this property.
|
60
|
+
isSingle() {
|
61
|
+
throw new Error('isSingle is abstract');
|
62
|
+
}
|
63
|
+
|
64
|
+
/**
|
65
|
+
* Return true if this vertex is connected to any others.
|
66
|
+
* @return {boolean}
|
67
|
+
*/
|
68
|
+
isConnected() {
|
69
|
+
return this.in.length != 0 || this.out.length != 0;
|
70
|
+
}
|
71
|
+
|
72
|
+
|
73
|
+
/**
|
74
|
+
* Connects an outbound edge from `this` to `child`. This method
|
75
|
+
* also creates the inbound edge from `child` to `this`, and is idempotent.
|
76
|
+
* @param child {BaseVertex}
|
77
|
+
*/
|
78
|
+
addChild(child) {
|
79
|
+
if(child === undefined){
|
80
|
+
console.error('tried adding undefined child to ' + this.toString());
|
81
|
+
return;
|
82
|
+
}
|
83
|
+
child.in.add(this);
|
84
|
+
this.out.add(child);
|
85
|
+
}
|
86
|
+
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Connects an outbound edge from `parent` to `this`. This method
|
90
|
+
* also creates the inbound edge from `this` to `parent`, and is idempotent.
|
91
|
+
* @param parent {BaseVertex}
|
92
|
+
*/
|
93
|
+
addParent(parent) {
|
94
|
+
if(parent === undefined){
|
95
|
+
console.error('tried adding undefined parent to ' + this.toString());
|
96
|
+
return;
|
97
|
+
}
|
98
|
+
parent.out.add(this);
|
99
|
+
this.in.add(parent);
|
100
|
+
}
|
101
|
+
|
102
|
+
/**
|
103
|
+
* The inverse of `addChild`.
|
104
|
+
* @param child {BaseVertex}
|
105
|
+
*/
|
106
|
+
removeChild(child) {
|
107
|
+
this.out.delete(child);
|
108
|
+
child.in.delete(this);
|
109
|
+
}
|
110
|
+
|
111
|
+
/**
|
112
|
+
* The inverse of `addParent`
|
113
|
+
* @param parent {BaseVertex}
|
114
|
+
*/
|
115
|
+
removeParent(parent) {
|
116
|
+
parent.out.delete(this);
|
117
|
+
this.in.delete(parent);
|
118
|
+
}
|
119
|
+
|
120
|
+
/**
|
121
|
+
* Disconnect this vertex from all adjacent vertices.
|
122
|
+
*/
|
123
|
+
unlink() {
|
124
|
+
// work on copies because we'll be modifying both.
|
125
|
+
var parents = this.in.toArray(),
|
126
|
+
children = this.out.toArray();
|
127
|
+
parents.forEach((p) => p.removeChild(this));
|
128
|
+
children.forEach((c) => c.removeParent(this));
|
129
|
+
// we have to do this to avoid a weird case when a list of
|
130
|
+
// vertices is being unlinked in an arbitrary order.
|
131
|
+
this.in.clear();
|
132
|
+
this.out.clear();
|
133
|
+
}
|
134
|
+
|
135
|
+
/**
|
136
|
+
* During collapse and expand operations, we need a simple way to
|
137
|
+
* replace connections to single vertices that have been collapsed into
|
138
|
+
* multi vertices to those multi vertices. We do this by constructing
|
139
|
+
* a map from vertex ids to the actual vertex that will represent that
|
140
|
+
* id in the final collapsed or expanded graph.
|
141
|
+
*
|
142
|
+
* @param idToActual {{String: BaseVertex}}
|
143
|
+
*/
|
144
|
+
reconnectWith(idToActual){
|
145
|
+
var parentIds = _.map(this.in.toArray(), (v) => v.id),
|
146
|
+
childIds = _.map(this.out.toArray(), (v) => v.id);
|
147
|
+
this.in.clear();
|
148
|
+
this.out.clear();
|
149
|
+
parentIds.forEach((pId) => this.addParent(idToActual[pId]));
|
150
|
+
childIds.forEach((cId) => this.addChild(idToActual[cId]));
|
151
|
+
}
|
152
|
+
|
153
|
+
// abstract and no op. Called to start collapsing
|
154
|
+
unlinkMembers() {
|
155
|
+
/* NOOP */
|
156
|
+
}
|
157
|
+
|
158
|
+
// called to finish collapsing -- no op in the base class
|
159
|
+
replaceMembers() {
|
160
|
+
/** NOOP */
|
161
|
+
}
|
162
|
+
|
163
|
+
/**
|
164
|
+
* Two vertices are the same if their in and out are the same
|
165
|
+
* and they have the same role kind.
|
166
|
+
* @param other {BaseVertex}
|
167
|
+
* @return {boolean}
|
168
|
+
*/
|
169
|
+
equivalent(other) {
|
170
|
+
return this.roleKind === other.roleKind &&
|
171
|
+
this.in.equals(other.in) &&
|
172
|
+
this.out.equals(other.out);
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
|
177
|
+
/**
|
178
|
+
* A vertex representing a single Conjur resource.
|
179
|
+
*/
|
180
|
+
class SingleVertex extends BaseVertex {
|
181
|
+
constructor(id) {
|
182
|
+
super(id);
|
183
|
+
}
|
184
|
+
|
185
|
+
isSingle() {
|
186
|
+
return true;
|
187
|
+
}
|
188
|
+
|
189
|
+
toString() {
|
190
|
+
return `V[${this.id}: in:${this.in.length} out:${this.out.length}]`;
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
194
|
+
/**
|
195
|
+
* A vertex representing a set of equivalent Conjur resources.
|
196
|
+
*/
|
197
|
+
class MultiVertex extends BaseVertex {
|
198
|
+
constructor(members) {
|
199
|
+
const initialMember = members[0];
|
200
|
+
|
201
|
+
super(nextId('mv-' + initialMember.roleKind + '-'));
|
202
|
+
this.roleKind = initialMember.roleKind;
|
203
|
+
|
204
|
+
// Don't connect to these vertices yet
|
205
|
+
this.pendingIn = initialMember.in.toArray();
|
206
|
+
this.pendingOut = initialMember.out.toArray();
|
207
|
+
|
208
|
+
|
209
|
+
// Our members.
|
210
|
+
this.members = newVertexSet();
|
211
|
+
this.members.addEach(members);
|
212
|
+
}
|
213
|
+
|
214
|
+
isSingle() {
|
215
|
+
return false;
|
216
|
+
}
|
217
|
+
|
218
|
+
/**
|
219
|
+
* Something very wrong is happening if this is called. Log the
|
220
|
+
* error and return false.
|
221
|
+
* @param o {BaseVertex}
|
222
|
+
* @returns {boolean}
|
223
|
+
*/
|
224
|
+
equivalent(o) {
|
225
|
+
console.error(`called equivalent(${o.toString()} on MultiVertex ${this.toString()}`);
|
226
|
+
return false;
|
227
|
+
}
|
228
|
+
|
229
|
+
reconnectWith(idToActual){
|
230
|
+
var parentIds = _.map(this.pendingIn, (v) => v.id),
|
231
|
+
childIds = _.map(this.pendingOut, (v) => v.id);
|
232
|
+
this.in.clear();
|
233
|
+
this.out.clear();
|
234
|
+
parentIds.forEach((pId) => {
|
235
|
+
var p = idToActual[pId];
|
236
|
+
this.addParent(p)
|
237
|
+
});
|
238
|
+
childIds.forEach((cId) => {
|
239
|
+
var c = idToActual[cId];
|
240
|
+
this.addChild(c)
|
241
|
+
});
|
242
|
+
}
|
243
|
+
|
244
|
+
unlinkMembers() {
|
245
|
+
// disconnect all of our members from the graph
|
246
|
+
this.members.forEach((m) => m.unlink());
|
247
|
+
this.unlink();
|
248
|
+
}
|
249
|
+
|
250
|
+
replaceMembers() {
|
251
|
+
this.pendingIn.forEach((a) => a.addChild(this));
|
252
|
+
this.pendingOut.forEach((a) => a.addParent(this));
|
253
|
+
}
|
254
|
+
|
255
|
+
toString(){
|
256
|
+
var {roleKind, pendingIn, pendingOut, members:{length}} = this;
|
257
|
+
return `MV[${this.id}: ${length} ${roleKind}s in: ${this.in.length} out: ${this.out.length}]`
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
export function normalizeEdges(edges, invert=false) {
|
262
|
+
return edges.map(
|
263
|
+
(e) => {
|
264
|
+
if(_.isArray(e)){
|
265
|
+
e = {parent: e[0], child: e[1]};
|
266
|
+
}
|
267
|
+
return invert ? {parent: e.child, child: e.parent}
|
268
|
+
: e;
|
269
|
+
}
|
270
|
+
);
|
271
|
+
}
|
272
|
+
|
273
|
+
export function verticesFromEdges(edges, invert=false) {
|
274
|
+
edges = normalizeEdges(edges, invert);
|
275
|
+
var vs = {};
|
276
|
+
var vertex = (id) => {
|
277
|
+
return vs[id] || (vs[id] = new SingleVertex(id));
|
278
|
+
};
|
279
|
+
edges.forEach(
|
280
|
+
({parent, child}) => {
|
281
|
+
const p = vertex(parent),
|
282
|
+
c = vertex(child);
|
283
|
+
p.addChild(c); // connects both.
|
284
|
+
}
|
285
|
+
);
|
286
|
+
return Object.values(vs);
|
287
|
+
}
|
288
|
+
|
289
|
+
export function edgesFromVertices(vertices) {
|
290
|
+
var edges = newEdgeSet();
|
291
|
+
vertices.forEach(
|
292
|
+
(v) => {
|
293
|
+
v.in.forEach(
|
294
|
+
(i) => edges.add({parent: i.id, child: v.id})
|
295
|
+
);
|
296
|
+
v.out.forEach(
|
297
|
+
(o) => edges.add({parent: v.id, child: o.id})
|
298
|
+
);
|
299
|
+
}
|
300
|
+
);
|
301
|
+
|
302
|
+
return edges.toArray();
|
303
|
+
}
|
304
|
+
|
305
|
+
export function collapse(vertices, cutoff = 10) {
|
306
|
+
|
307
|
+
// initially, everything is actually itself.
|
308
|
+
var idToActual = {};
|
309
|
+
vertices.forEach((v) => idToActual[v.id] = v);
|
310
|
+
|
311
|
+
var collapsed = [];
|
312
|
+
vertices.forEach(
|
313
|
+
(v) => {
|
314
|
+
var equiv = _.find(collapsed, (vs) => vs[0].equivalent(v));
|
315
|
+
if (!equiv) {
|
316
|
+
equiv = [v];
|
317
|
+
collapsed.push(equiv);
|
318
|
+
} else {
|
319
|
+
equiv.push(v);
|
320
|
+
}
|
321
|
+
}
|
322
|
+
);
|
323
|
+
|
324
|
+
function createMulti(vs){
|
325
|
+
var m = new MultiVertex(vs);
|
326
|
+
// replace the 'actual' for this id with 'm'.
|
327
|
+
vs.forEach(({id}) => idToActual[id] = m);
|
328
|
+
return m;
|
329
|
+
}
|
330
|
+
|
331
|
+
var result = _.reduce(
|
332
|
+
collapsed,
|
333
|
+
(accum, o) => o.length <= cutoff ? accum.concat(o) : accum.concat([createMulti(o)]),
|
334
|
+
[]
|
335
|
+
);
|
336
|
+
|
337
|
+
// Now we can replace in/out edges with their actual vertex.
|
338
|
+
var survivors = newVertexSet();
|
339
|
+
survivors.addEach(Object.values(idToActual));
|
340
|
+
|
341
|
+
|
342
|
+
survivors.forEach(
|
343
|
+
(v) => {
|
344
|
+
v.reconnectWith(idToActual);
|
345
|
+
}
|
346
|
+
);
|
347
|
+
|
348
|
+
return survivors.toArray();
|
349
|
+
}
|
350
|
+
|
351
|
+
export function newEdgeSet(){
|
352
|
+
return new Set(null, // members
|
353
|
+
(a, b) => a.parent == b.parent && a.child == b.child, // equals
|
354
|
+
(a) => `${a.parent}->${a.child}` // hash
|
355
|
+
);
|
356
|
+
}
|
357
|
+
|
358
|
+
export function isInternal(roleid) {
|
359
|
+
return roleid.indexOf('@') >= 0;
|
360
|
+
}
|
361
|
+
|
362
|
+
export var __test__ = {
|
363
|
+
roleKindFromId,
|
364
|
+
newVertexSet,
|
365
|
+
MultiVertex,
|
366
|
+
SingleVertex,
|
367
|
+
nextId
|
368
|
+
};
|
@@ -0,0 +1,153 @@
|
|
1
|
+
var React = require('react'),
|
2
|
+
Fluxxor = require('fluxxor');
|
3
|
+
|
4
|
+
var FluxMixin = Fluxxor.FluxMixin(React),
|
5
|
+
StoreWatchMixin = Fluxxor.StoreWatchMixin;
|
6
|
+
|
7
|
+
var TabbedArea = require('react-bootstrap/lib/TabbedArea'),
|
8
|
+
TabPane = require('react-bootstrap/lib/TabPane');
|
9
|
+
|
10
|
+
var TabMixin = require('../generic/tab_mixin'),
|
11
|
+
Breadcrumbs = require('../generic/breadcrumbs'),
|
12
|
+
RoleLink = require('../generic/role_link'),
|
13
|
+
RoleGraph = require('../graph/graph');
|
14
|
+
|
15
|
+
import {getTabname} from '../../utils';
|
16
|
+
|
17
|
+
var AuditTable = require('../audit/table');
|
18
|
+
|
19
|
+
import {compact, map} from 'lodash';
|
20
|
+
|
21
|
+
export default React.createClass({
|
22
|
+
displayName: 'GroupView',
|
23
|
+
|
24
|
+
mixins: [FluxMixin, StoreWatchMixin('audit', 'group', 'resources', 'route'), TabMixin],
|
25
|
+
|
26
|
+
askForNewData(state) {
|
27
|
+
var actions = this.getFlux().actions;
|
28
|
+
|
29
|
+
actions.audit.loadForRole('group', state.id);
|
30
|
+
actions.audit.loadForResource('group', state.id);
|
31
|
+
actions.group.load(state.id);
|
32
|
+
actions.resources.loadOne('group', state.id);
|
33
|
+
},
|
34
|
+
|
35
|
+
componentDidMount() {
|
36
|
+
this.askForNewData(this.state);
|
37
|
+
},
|
38
|
+
|
39
|
+
componentWillUpdate(nextProps, nextState) {
|
40
|
+
if (this.state.id !== nextState.id) {
|
41
|
+
this.askForNewData(nextState);
|
42
|
+
}
|
43
|
+
|
44
|
+
return true;
|
45
|
+
},
|
46
|
+
|
47
|
+
getStateFromFlux() {
|
48
|
+
var flux = this.getFlux(),
|
49
|
+
id = flux.store('route').getParam('id'),
|
50
|
+
audit = flux.store('audit').getEventFor('group', id),
|
51
|
+
data = flux.store('group').getData(),
|
52
|
+
resource = flux.store('resources').getResource('group', id);
|
53
|
+
|
54
|
+
var ret = {
|
55
|
+
id: id,
|
56
|
+
loading: data.loading,
|
57
|
+
group: data.group,
|
58
|
+
owned: data.owned,
|
59
|
+
roles: data.roles,
|
60
|
+
owner: resource.data.owner,
|
61
|
+
annotations: resource.data.annotations,
|
62
|
+
resource: resource.data,
|
63
|
+
members: data.members,
|
64
|
+
resources: data.resources,
|
65
|
+
audit: audit.data
|
66
|
+
};
|
67
|
+
|
68
|
+
ret.loading.audit = audit.loading;
|
69
|
+
ret.loading.owner = resource.loading;
|
70
|
+
ret.loading.annotations = resource.loading;
|
71
|
+
|
72
|
+
return ret;
|
73
|
+
},
|
74
|
+
|
75
|
+
render() {
|
76
|
+
var members = map(this.state.members, function(member) {
|
77
|
+
return (
|
78
|
+
<li className="list-group-item list-group-item-noborder">
|
79
|
+
<RoleLink id={member.member} />
|
80
|
+
</li>
|
81
|
+
);
|
82
|
+
});
|
83
|
+
|
84
|
+
// TODO: refactor (common code should go to mixins)
|
85
|
+
// TODO: memberships
|
86
|
+
// TODO: controls for members management
|
87
|
+
var overviewTab = (
|
88
|
+
<TabPane eventKey="overview" tab="Overview">
|
89
|
+
<dl className="dl-horizontal">
|
90
|
+
<dt>Created by</dt>
|
91
|
+
<dd><RoleLink id={this.state.group.userid} /></dd>
|
92
|
+
|
93
|
+
<dt>Owner</dt>
|
94
|
+
<dd><RoleLink id={this.state.owner} /></dd>
|
95
|
+
</dl>
|
96
|
+
</TabPane>
|
97
|
+
);
|
98
|
+
|
99
|
+
var membersTab = (
|
100
|
+
<TabPane eventKey="members" tab={getTabname('Members', members)}>
|
101
|
+
<ul className="list-group">
|
102
|
+
{members}
|
103
|
+
</ul>
|
104
|
+
</TabPane>
|
105
|
+
);
|
106
|
+
|
107
|
+
var ownedTab = this.ownedTab(),
|
108
|
+
membershipsTab = this.membershipsTab(this.state.group.roleid),
|
109
|
+
permissionsTab = this.permissionsTab(this.state.group.roleid),
|
110
|
+
annotationsTab = this.annotationsTab();
|
111
|
+
|
112
|
+
var auditTab = (
|
113
|
+
<TabPane eventKey="audit" tab="Recent Activity">
|
114
|
+
<div className="audit auditGroup">
|
115
|
+
<AuditTable events={this.state.audit}
|
116
|
+
caption=""
|
117
|
+
isLoading={this.state.loading.audit} />
|
118
|
+
</div>
|
119
|
+
</TabPane>
|
120
|
+
);
|
121
|
+
|
122
|
+
var tabs = compact([
|
123
|
+
overviewTab,
|
124
|
+
membersTab,
|
125
|
+
ownedTab,
|
126
|
+
membershipsTab,
|
127
|
+
permissionsTab,
|
128
|
+
annotationsTab,
|
129
|
+
auditTab
|
130
|
+
]);
|
131
|
+
|
132
|
+
var elems = [
|
133
|
+
{url: '/ui/groups', text: 'Groups'},
|
134
|
+
{url: '', text: this.state.group.id},
|
135
|
+
{url: '', text: '(owned by ' + this.state.group.ownerid + ')'}
|
136
|
+
];
|
137
|
+
|
138
|
+
return (
|
139
|
+
<div className="b-group">
|
140
|
+
<Breadcrumbs elems={elems} />
|
141
|
+
<hr />
|
142
|
+
<h2>Group {this.state.group.id}</h2>
|
143
|
+
<TabbedArea defaultActiveKey="overview">
|
144
|
+
{tabs}
|
145
|
+
</TabbedArea>
|
146
|
+
<hr />
|
147
|
+
<RoleGraph height="400"
|
148
|
+
kind="group"
|
149
|
+
id={this.state.id} />
|
150
|
+
</div>
|
151
|
+
);
|
152
|
+
}
|
153
|
+
});
|