conjur-asset-ui-api 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.git-hooks/pre_commit/ensure_livescript_compiled.rb +31 -0
- data/.git-hooks/pre_commit/trailing_whitespace.rb +26 -0
- data/.gitignore +20 -0
- data/.overcommit.yml +5 -0
- data/.project +18 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +41 -0
- data/Rakefile +1 -0
- data/compile_ls +2 -0
- data/conjur-asset-ui.gemspec +36 -0
- data/lib/conjur-asset-ui-version.rb +7 -0
- data/lib/conjur-asset-ui.rb +7 -0
- data/lib/conjur/audit/follower.rb +63 -0
- data/lib/conjur/audit/humanizer.rb +53 -0
- data/lib/conjur/audit/tableizer.rb +55 -0
- data/lib/conjur/command/ui.rb +38 -0
- data/lib/conjur/webserver/api_proxy.rb +94 -0
- data/lib/conjur/webserver/audit_stream.rb +92 -0
- data/lib/conjur/webserver/authorize.rb +28 -0
- data/lib/conjur/webserver/conjur_info.rb +33 -0
- data/lib/conjur/webserver/home.rb +36 -0
- data/lib/conjur/webserver/login.rb +50 -0
- data/lib/conjur/webserver/server.rb +111 -0
- data/livescript/views/audit.ls +124 -0
- data/public/css/bootstrap.css +7 -0
- data/public/css/styles.less +400 -0
- data/public/fonts/glyphicons-halflings-regular.eot +0 -0
- data/public/fonts/glyphicons-halflings-regular.svg +229 -0
- data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/public/fonts/glyphicons-halflings-regular.woff +0 -0
- data/public/images/conjur-logo.svg +26 -0
- data/public/images/icon-client-pc.svg +12 -0
- data/public/images/icon-environment.png +0 -0
- data/public/images/icon-person.svg +12 -0
- data/public/images/icon-service-dots.svg +13 -0
- data/public/images/icon-variable.png +0 -0
- data/public/index.html +121 -0
- data/public/js/lib/JSXTransformer.js +10862 -0
- data/public/js/lib/async.js +958 -0
- data/public/js/lib/backbone.js +2 -0
- data/public/js/lib/bootstrap.js +6 -0
- data/public/js/lib/date.extensions.js +141 -0
- data/public/js/lib/less.js +16 -0
- data/public/js/lib/moment.js +7768 -0
- data/public/js/lib/pace.js +2 -0
- data/public/js/lib/prelude-browser-min.js +1 -0
- data/public/js/lib/react-with-addons.js +15505 -0
- data/public/js/lib/react.js +14469 -0
- data/public/js/lib/sorted-set.no-require.js +1170 -0
- data/public/js/lib/sorted-set.no-require.js.txt +6 -0
- data/public/js/lib/underscore-min.js +6 -0
- data/public/js/lib/underscore.string.min.js +1 -0
- data/public/js/main.js +353 -0
- data/public/js/models/namespace.js +6 -0
- data/public/js/models/policyList.js +10 -0
- data/public/js/models/record.js +26 -0
- data/public/js/models/resourceList.js +61 -0
- data/public/js/models/userList.js +16 -0
- data/public/js/models/variableList.js +12 -0
- data/public/js/views/audit.js +191 -0
- data/public/js/views/dashboard.js +35 -0
- data/public/js/views/generic.js +42 -0
- data/public/js/views/group.js +32 -0
- data/public/js/views/groups.js +18 -0
- data/public/js/views/host.js +40 -0
- data/public/js/views/hosts.js +18 -0
- data/public/js/views/layer.js +63 -0
- data/public/js/views/layers.js +18 -0
- data/public/js/views/mixins/search.js +9 -0
- data/public/js/views/namespaces.js +40 -0
- data/public/js/views/navSearch.js +16 -0
- data/public/js/views/permissions.js +91 -0
- data/public/js/views/policies.js +17 -0
- data/public/js/views/policy.js +23 -0
- data/public/js/views/resource.js +23 -0
- data/public/js/views/role.js +18 -0
- data/public/js/views/searchResults.js +146 -0
- data/public/js/views/time.js +14 -0
- data/public/js/views/user.js +22 -0
- data/public/js/views/users.js +18 -0
- data/public/js/views/variable.js +41 -0
- data/public/js/views/variables.js +18 -0
- data/vendor/prelude-ls/.gitignore +2 -0
- data/vendor/prelude-ls/.travis.yml +3 -0
- data/vendor/prelude-ls/CHANGELOG.md +81 -0
- data/vendor/prelude-ls/LICENSE +22 -0
- data/vendor/prelude-ls/Makefile +50 -0
- data/vendor/prelude-ls/README.md +15 -0
- data/vendor/prelude-ls/browser/prelude-browser-min.js +1 -0
- data/vendor/prelude-ls/browser/prelude-browser.js +1172 -0
- data/vendor/prelude-ls/lib/Func.js +40 -0
- data/vendor/prelude-ls/lib/List.js +602 -0
- data/vendor/prelude-ls/lib/Num.js +129 -0
- data/vendor/prelude-ls/lib/Obj.js +153 -0
- data/vendor/prelude-ls/lib/Str.js +68 -0
- data/vendor/prelude-ls/lib/index.js +164 -0
- data/vendor/prelude-ls/package.json +50 -0
- data/vendor/prelude-ls/package.ls +46 -0
- data/vendor/prelude-ls/src/Func.ls +17 -0
- data/vendor/prelude-ls/src/List.ls +299 -0
- data/vendor/prelude-ls/src/Num.ls +83 -0
- data/vendor/prelude-ls/src/Obj.ls +61 -0
- data/vendor/prelude-ls/src/Str.ls +32 -0
- data/vendor/prelude-ls/src/index.ls +56 -0
- data/vendor/prelude-ls/test/Func.ls +36 -0
- data/vendor/prelude-ls/test/List.ls +751 -0
- data/vendor/prelude-ls/test/Num.ls +258 -0
- data/vendor/prelude-ls/test/Obj.ls +145 -0
- data/vendor/prelude-ls/test/Prelude.ls +49 -0
- data/vendor/prelude-ls/test/Str.ls +208 -0
- data/vendor/prelude-ls/test/browser.html +5 -0
- metadata +328 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** @jsx React.DOM */
|
|
2
|
+
|
|
3
|
+
var LayerBox = React.createClass({
|
|
4
|
+
getInitialState: function() {
|
|
5
|
+
return { currentNamespace: "", members: [] };
|
|
6
|
+
},
|
|
7
|
+
render: function() {
|
|
8
|
+
return (
|
|
9
|
+
<div className="layerBox">
|
|
10
|
+
<NamespaceFilter currentNamespace={this.state.currentNamespace} namespaces={this.props.data.namespaces} />
|
|
11
|
+
<div className="layerList">
|
|
12
|
+
<h2>Layers</h2>
|
|
13
|
+
<GenericList data={{kind: "layers", members: this.state.members}} />
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** @jsx React.DOM */
|
|
2
|
+
|
|
3
|
+
var NamespaceFilter = React.createClass({
|
|
4
|
+
render: function() {
|
|
5
|
+
return(
|
|
6
|
+
<div className="namespaceList">
|
|
7
|
+
<h2>Namespace filter:</h2>
|
|
8
|
+
{ this.transferPropsTo(<NamespaceList />) }
|
|
9
|
+
</div>
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
var NamespaceListItem = React.createClass({
|
|
15
|
+
render: function() {
|
|
16
|
+
return (
|
|
17
|
+
<option value={this.props.id}>{this.props.id}</option>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
var NamespaceList = React.createClass({
|
|
23
|
+
handleChange: function(e) {
|
|
24
|
+
var value = e.target.options[e.target.selectedIndex].value;
|
|
25
|
+
updateNamespace(value);
|
|
26
|
+
},
|
|
27
|
+
render: function() {
|
|
28
|
+
var namespaces = this.props.namespaces.map(function (namespace) {
|
|
29
|
+
return <NamespaceListItem id={namespace}/>;
|
|
30
|
+
}).sort();
|
|
31
|
+
return (
|
|
32
|
+
<div>
|
|
33
|
+
<select onChange={this.handleChange} value={this.props.currentNamespace}>
|
|
34
|
+
<option value="" />
|
|
35
|
+
{namespaces}
|
|
36
|
+
</select>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**@jsx React.DOM*/
|
|
2
|
+
|
|
3
|
+
var NavSearchForm = React.createClass({
|
|
4
|
+
mixins: [ SearchMixin ],
|
|
5
|
+
|
|
6
|
+
render: function(){
|
|
7
|
+
return (
|
|
8
|
+
<form className="form-inline navbar-form" role="search" onSubmit={this.handleSubmit}>
|
|
9
|
+
<div className="form-group">
|
|
10
|
+
<input ref="input" type="text" className="form-control" placeholder="Search Conjur"></input>
|
|
11
|
+
</div>
|
|
12
|
+
<button type="submit" className="btn btn-default search-button">Search</button>
|
|
13
|
+
</form>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/** @jsx React.DOM */
|
|
2
|
+
|
|
3
|
+
// Renders a permission as a tr
|
|
4
|
+
var PermissionRow = React.createClass({
|
|
5
|
+
render: function(){
|
|
6
|
+
var p = this.props.data;
|
|
7
|
+
return (<tr>
|
|
8
|
+
<td> { p.privilege } </td>
|
|
9
|
+
<td> { p.grant_option ? "yes" : "no" }</td>
|
|
10
|
+
<td> <RoleLink id={p.grantor}/> </td>
|
|
11
|
+
</tr>);
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
var Permissions = React.createClass({
|
|
17
|
+
getInitialState: function(){
|
|
18
|
+
return {resources: [], loaded: false}
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
componentWillMount: function(){
|
|
22
|
+
$.get(this.url(), function(data){
|
|
23
|
+
this.setState({resources: data, loaded: true});
|
|
24
|
+
}.bind(this));
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
render: function(){
|
|
28
|
+
|
|
29
|
+
var rows = [];
|
|
30
|
+
var resources = _.sortBy(this.state.resources, function(r){
|
|
31
|
+
return r.id.split(':')[2];
|
|
32
|
+
});
|
|
33
|
+
resources.forEach(function(r){
|
|
34
|
+
var rowspan = r.permissions.length + 1;
|
|
35
|
+
var parts = r.id.split(":");
|
|
36
|
+
var id = parts[2];
|
|
37
|
+
var kind = parts[1]; // ignore env?
|
|
38
|
+
var cells = [
|
|
39
|
+
<td rowSpan={rowspan}> <ResourceLink data={r.id}/> </td>,
|
|
40
|
+
<td rowSpan={rowspan}> {kind} </td>
|
|
41
|
+
];
|
|
42
|
+
if(rowspan == 1){
|
|
43
|
+
cells.push(<td colSpan="3"> full permissions </td>);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
rows.push(
|
|
47
|
+
<tr key={r.id}>{cells}</tr>
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if(rowspan > 1){
|
|
51
|
+
rows.push(r.permissions.map(function(p){
|
|
52
|
+
return <PermissionRow data={p}/>
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
});
|
|
57
|
+
rows = _.flatten(rows);
|
|
58
|
+
|
|
59
|
+
console.log("rows", rows);
|
|
60
|
+
|
|
61
|
+
var contents;
|
|
62
|
+
if (this.state.loaded) {
|
|
63
|
+
contents = (<table>
|
|
64
|
+
<thead>
|
|
65
|
+
<tr>
|
|
66
|
+
<th> Resource </th>
|
|
67
|
+
<th> Kind </th>
|
|
68
|
+
<th> Privilege </th>
|
|
69
|
+
<th> Can Grant? </th>
|
|
70
|
+
<th> Granted By </th>
|
|
71
|
+
</tr>
|
|
72
|
+
</thead>
|
|
73
|
+
<tbody> {rows} </tbody>
|
|
74
|
+
</table>);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
var cx = React.addons.classSet;
|
|
78
|
+
var classes = cx({
|
|
79
|
+
permissions: true,
|
|
80
|
+
loading: !this.state.loaded
|
|
81
|
+
});
|
|
82
|
+
return <section className={classes}>
|
|
83
|
+
<h3>Permissions held by {this.props.role}</h3>
|
|
84
|
+
{contents}
|
|
85
|
+
</section>;
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
url: function(){
|
|
89
|
+
return "/api/authz/" + conjurConfiguration.account + "/resources?acting_as=" + this.props.role;
|
|
90
|
+
}
|
|
91
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** @jsx React.DOM */
|
|
2
|
+
|
|
3
|
+
var PolicyBox = React.createClass({
|
|
4
|
+
getInitialState: function() {
|
|
5
|
+
return { currentNamespace: "", members: [] };
|
|
6
|
+
},
|
|
7
|
+
render: function() {
|
|
8
|
+
return (
|
|
9
|
+
<div className="policyBox">
|
|
10
|
+
<div className="policyList">
|
|
11
|
+
<h2>Policies</h2>
|
|
12
|
+
<GenericList data={{kind: "policies", members: this.state.members}} />
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** @jsx React.DOM */
|
|
2
|
+
|
|
3
|
+
var Policy = React.createClass({
|
|
4
|
+
render: function() {
|
|
5
|
+
var id = this.props.policy.id.split(':')[2];
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<div className="variable">
|
|
9
|
+
<h2>Policy {id}</h2>
|
|
10
|
+
<dl>
|
|
11
|
+
<dt>Owner</dt>
|
|
12
|
+
<dd><RoleLink id={this.props.policy.id}/></dd>
|
|
13
|
+
</dl>
|
|
14
|
+
|
|
15
|
+
<Permissions role={this.props.policy.id}/>
|
|
16
|
+
|
|
17
|
+
<div className="audit auditPolicy">
|
|
18
|
+
<AuditBox roles={[this.props.policy.id]} />
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** @jsx React.DOM */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
Renders a link to the resource with id given by this.props.data.
|
|
5
|
+
|
|
6
|
+
Includes a slick little icon for the following kinds:
|
|
7
|
+
TODO which kinds?
|
|
8
|
+
**/
|
|
9
|
+
var ResourceLink = React.createClass({
|
|
10
|
+
render: function(){
|
|
11
|
+
var resourceId = this.props.data;
|
|
12
|
+
var tokens = resourceId.split(':');
|
|
13
|
+
var kind = tokens[1];
|
|
14
|
+
var id = tokens[tokens.length - 1];
|
|
15
|
+
var text = this.props.text || id;
|
|
16
|
+
var href = "/ui/" + pluralize(kind) + "/" + encodeURIComponent(id);
|
|
17
|
+
var classes = ['resource-link'];
|
|
18
|
+
if(!this.props.noIcon){
|
|
19
|
+
classes.push(kind);
|
|
20
|
+
}
|
|
21
|
+
return <a className={classes.join(' ')} href={href}>{text}</a>
|
|
22
|
+
}
|
|
23
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** @jsx React.DOM */
|
|
2
|
+
|
|
3
|
+
/** render a link to the role represented by this.props.id
|
|
4
|
+
Example: <RoleLink id="ci:user:jon"/>
|
|
5
|
+
*/
|
|
6
|
+
var RoleLink = React.createClass({
|
|
7
|
+
render: function() {
|
|
8
|
+
var tokens = this.props.id.split(":");
|
|
9
|
+
var kind = tokens[1];
|
|
10
|
+
var id = tokens[tokens.length-1];
|
|
11
|
+
var href = "/ui/" + pluralize(kind) + "/" + encodeURIComponent(id);
|
|
12
|
+
var classes = ['role-link'];
|
|
13
|
+
if(!this.props.noIcon) classes.push(kind)
|
|
14
|
+
return <a className={classes.join(' ')} href={href}>
|
|
15
|
+
{id}
|
|
16
|
+
</a>;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**@jsx React.DOM*/
|
|
2
|
+
|
|
3
|
+
var SearchGroupTitle = React.createClass({
|
|
4
|
+
render: function(){
|
|
5
|
+
return <span>{this.title()}</span>
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
title: function(){
|
|
9
|
+
var words = this.props.data.kind.replace(/[-_]/, ' ').split(' ');
|
|
10
|
+
words[words.length - 1] = pluralize(words[words.length - 1]);
|
|
11
|
+
return words.map(_.str.capitalize).join(' ') + ' (' + this.props.data.items.length + ')';
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
var SearchGroupHeading = React.createClass({
|
|
16
|
+
render: function(){
|
|
17
|
+
var targetId = "#search-collapse-" + this.props.data.kind;
|
|
18
|
+
return <div className="panel-heading">
|
|
19
|
+
<h4 className="panel-title">
|
|
20
|
+
<a data-toggle="collapse" data-target={targetId}
|
|
21
|
+
className={'group-heading' + this.props.data.kind}>
|
|
22
|
+
<SearchGroupTitle data={this.props.data} />
|
|
23
|
+
</a>
|
|
24
|
+
</h4>
|
|
25
|
+
</div>;
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
title: function(){
|
|
29
|
+
var words = this.props.data.kind.replace(/[-_]/, ' ').split(' ');
|
|
30
|
+
words[words.length - 1] = pluralize(words[words.length - 1]);
|
|
31
|
+
return words.map(_.str.capitalize).join(' ') + ' (' + this.props.data.items.length + ')';
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
var SearchResultItem = React.createClass({
|
|
36
|
+
render: function(){
|
|
37
|
+
return <div className="item">
|
|
38
|
+
<h4> { this.titleLink() } </h4>
|
|
39
|
+
<div className="details">
|
|
40
|
+
<strong> ID: </strong> <ResourceLink data={this.props.data.id} noIcon="true"/>
|
|
41
|
+
<strong> Owner: </strong> <RoleLink id={this.props.data.owner} noIcon="true"/>
|
|
42
|
+
</div>
|
|
43
|
+
<div className="comment">
|
|
44
|
+
{this.commentText()}
|
|
45
|
+
</div>
|
|
46
|
+
</div>;
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
commentText: function(){
|
|
50
|
+
var annots = this.annotationsMap();
|
|
51
|
+
return annots['description'] || "";
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
titleLink: function(){
|
|
55
|
+
var annots = this.annotationsMap();
|
|
56
|
+
return <ResourceLink data={this.props.data.id} text={annots['name']}/>
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
annotationsMap: function(){
|
|
60
|
+
if(this._annotationsMap) return this._annotationsMap;
|
|
61
|
+
var map = this._annotationsMap = {};
|
|
62
|
+
(this.props.data.annotations || []).forEach(function(a){
|
|
63
|
+
map[a.name] = a.value;
|
|
64
|
+
});
|
|
65
|
+
return map;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// accepts props like data: { kind:"", items:[] }
|
|
70
|
+
var SearchGroup = React.createClass({
|
|
71
|
+
render: function(){
|
|
72
|
+
var id = "search-group-" + this.props.data.kind;
|
|
73
|
+
var items = this.props.data.items.map(function(r){
|
|
74
|
+
return <SearchResultItem data={r}/>
|
|
75
|
+
})
|
|
76
|
+
return <div id={id} className="panel panel-default search-group">
|
|
77
|
+
<SearchGroupHeading data={this.props.data}/>
|
|
78
|
+
<div id={"search-collapse-" + this.props.data.kind} className="panel-collapse collapse in">
|
|
79
|
+
<div className="panel-body">
|
|
80
|
+
{items}
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
var SearchResults = React.createClass({
|
|
88
|
+
render: function() {
|
|
89
|
+
var results = this.props.data.results;
|
|
90
|
+
var grouped = _.groupBy(results, function(r){
|
|
91
|
+
return r.id.split(':')[1];
|
|
92
|
+
});
|
|
93
|
+
// Don't care about these
|
|
94
|
+
delete grouped['environment-variables']
|
|
95
|
+
delete grouped['notification']
|
|
96
|
+
delete grouped['queue']
|
|
97
|
+
|
|
98
|
+
var groups = _.map(grouped,function(items, key){
|
|
99
|
+
var data = {items:items, kind: key}; // - prevent editor barfing
|
|
100
|
+
return <SearchGroup data={data}/>
|
|
101
|
+
});
|
|
102
|
+
var scores = {
|
|
103
|
+
'policy': 0,
|
|
104
|
+
'layer': 1,
|
|
105
|
+
'group': 2,
|
|
106
|
+
'host': 3,
|
|
107
|
+
'user': 4,
|
|
108
|
+
'variable': 5,
|
|
109
|
+
'key_pair': 6
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
groups.sort(function(a, b) {
|
|
113
|
+
return ( scores[a.props.data.kind] || 100 ) - ( scores[b.props.data.kind] || 100 );
|
|
114
|
+
});
|
|
115
|
+
var toc = groups.map(function(g) {
|
|
116
|
+
var gid = "#search-group-" + g.props.data.kind;
|
|
117
|
+
return <div className="toc-item">
|
|
118
|
+
<a href={gid}><SearchGroupTitle data={g.props.data} /></a>
|
|
119
|
+
</div>
|
|
120
|
+
});
|
|
121
|
+
var heading = "Found " + this.props.data.results.length +
|
|
122
|
+
" resources matching \"" + this.props.data.search + "\"";
|
|
123
|
+
return (
|
|
124
|
+
<div id="searchResults">
|
|
125
|
+
<div className="searchResults">
|
|
126
|
+
<h3> { heading } </h3>
|
|
127
|
+
<div className="search-results-toc">
|
|
128
|
+
{toc}
|
|
129
|
+
</div>
|
|
130
|
+
<div className="search-results">
|
|
131
|
+
{groups}
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
SearchResults.search = function(search, container){
|
|
140
|
+
container = container || document.getElementById('content');
|
|
141
|
+
$.get(endpoints.authz("resources", {search: search}), function(results){
|
|
142
|
+
|
|
143
|
+
var data = {search: search, results: results};
|
|
144
|
+
React.renderComponent(<SearchResults data={data}/>, container);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** @jsx React.DOM */
|
|
2
|
+
|
|
3
|
+
// render a <time> tag. props.timestamp should be a timestamp
|
|
4
|
+
// that moment can parse, props.format should be one of the
|
|
5
|
+
// format strings accepted by moment (optional).
|
|
6
|
+
var Time = React.createClass({
|
|
7
|
+
render: function(){
|
|
8
|
+
var timestamp = this.props.timestamp;
|
|
9
|
+
var format = this.props.format || 'lll';
|
|
10
|
+
var m = moment(timestamp);
|
|
11
|
+
|
|
12
|
+
return <time datetime={m.format()}>{m.format(format)}</time>;
|
|
13
|
+
}
|
|
14
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** @jsx React.DOM */
|
|
2
|
+
|
|
3
|
+
var User = React.createClass({
|
|
4
|
+
render: function(){
|
|
5
|
+
var user = this.props.data;
|
|
6
|
+
console.log("render User data=", user);
|
|
7
|
+
return (
|
|
8
|
+
<div className="user">
|
|
9
|
+
<h2> User {user.login} </h2>
|
|
10
|
+
<dl>
|
|
11
|
+
<dt> Owner </dt>
|
|
12
|
+
<dd><RoleLink id={user.ownerid}/></dd>
|
|
13
|
+
</dl>
|
|
14
|
+
<Permissions role={user.roleid}/>
|
|
15
|
+
<div className="audit auditGroup">
|
|
16
|
+
<AuditBox roles={[user.roleid]} resources={[user.resource_identifier]}/>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
});
|