conjur-asset-ui-api 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +2 -0
- data/compile_ls +5 -1
- data/conjur-asset-ui.gemspec +1 -0
- data/lib/conjur-asset-ui-version.rb +1 -1
- data/lib/conjur/webserver/home.rb +19 -13
- data/lib/conjur/webserver/login.rb +7 -0
- data/lib/conjur/webserver/renderer.rb +34 -0
- data/livescript/views/audit.ls +31 -19
- data/public/_client_code.html +37 -0
- data/public/_client_libs.html +21 -0
- data/public/css/styles.less +23 -10
- data/public/index.html.erb +71 -0
- data/public/js/main.js +33 -70
- data/public/js/models/groupRecord.js +35 -0
- data/public/js/models/hostRecord.js +26 -0
- data/public/js/models/layerRecord.js +38 -0
- data/public/js/models/record.js +36 -23
- data/public/js/models/userRecord.js +26 -0
- data/public/js/models/variableRecord.js +39 -0
- data/public/js/views/audit.js +53 -33
- data/public/js/views/dashboard.js +32 -5
- data/public/js/views/group.js +2 -1
- data/public/js/views/host.js +3 -2
- data/public/js/views/layer.js +3 -3
- data/public/js/views/owned.js +77 -0
- data/public/js/views/permissions.js +98 -36
- data/public/js/views/resource.js +3 -0
- data/public/js/views/user.js +3 -4
- data/spec/javascripts/helpers/.gitkeep +0 -0
- data/spec/javascripts/support/jasmine.yml +112 -0
- data/spec/javascripts/support/jasmine_helper.rb +22 -0
- data/spec/javascripts/support/run.html.erb +23 -0
- data/spec/javascripts/views/AuditSpec.js +22 -0
- data/spec/javascripts/views/AuditSpec.ls +18 -0
- metadata +39 -4
- data/public/index.html +0 -121
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7411cf0ff97005386a475d49c0fc2dfc015504c0
|
4
|
+
data.tar.gz: f0f45c605c547afa2d6216472d0c04967b3318c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56229687c796bffce448229fd4711c99bb78193b1cf2751c99d50d43a98e0bb0a4fa1fe0c5518d0709ce2e77ebfddf7d1e7368fbc5531da5ac3126c64cc0ce9e
|
7
|
+
data.tar.gz: 397fb524b5499af16448d2f3496a469054aa18ca77d64b1bfb1562329bbf44960a410f6968d893e1438f8977c4c2bd9977c09b3bdd843217af60a2939ede437a
|
data/Rakefile
CHANGED
data/compile_ls
CHANGED
data/conjur-asset-ui.gemspec
CHANGED
@@ -2,35 +2,41 @@ require 'time'
|
|
2
2
|
require 'rack/utils'
|
3
3
|
require 'rack/mime'
|
4
4
|
|
5
|
+
require 'conjur/webserver/renderer'
|
6
|
+
|
5
7
|
module Conjur
|
6
8
|
module WebServer
|
7
9
|
class Home
|
8
|
-
F = ::File
|
9
|
-
|
10
10
|
def initialize(root)
|
11
11
|
@root = root
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
# From Rack::File
|
15
15
|
def call(env)
|
16
|
-
path = File.expand_path(
|
16
|
+
path = File.expand_path(INDEX, @root)
|
17
|
+
renderer = Renderer.new @root
|
18
|
+
|
19
|
+
page = renderer.render File.read(path)
|
20
|
+
files = renderer.files + [path]
|
21
|
+
|
22
|
+
last_modified = files.map(&File.method(:mtime)).max.httpdate
|
17
23
|
|
18
|
-
if env["REQUEST_METHOD"] == "OPTIONS"
|
19
|
-
return [200, {'Allow' => ALLOW_HEADER, 'Content-Length' => '0'}, []]
|
20
|
-
end
|
21
|
-
last_modified = F.mtime(path).httpdate
|
22
24
|
return [304, {}, []] if env['HTTP_IF_MODIFIED_SINCE'] == last_modified
|
23
25
|
|
24
|
-
size =
|
25
|
-
|
26
|
-
headers = {
|
26
|
+
size = Rack::Utils.bytesize(page)
|
27
|
+
|
28
|
+
headers = {
|
27
29
|
"Last-Modified" => last_modified,
|
28
30
|
"Content-Type" => "text/html",
|
29
31
|
"Content-Length" => size.to_s
|
30
32
|
}
|
31
|
-
|
32
|
-
[
|
33
|
+
|
34
|
+
[200, headers, env["REQUEST_METHOD"] == "HEAD" ? [] : [page]]
|
33
35
|
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
INDEX = 'index.html.erb'.freeze
|
34
40
|
end
|
35
41
|
end
|
36
42
|
end
|
@@ -10,6 +10,12 @@ module Conjur
|
|
10
10
|
|
11
11
|
def call(env)
|
12
12
|
if sessionid = token_valid?(env)
|
13
|
+
require 'conjur/authn'
|
14
|
+
require 'base64'
|
15
|
+
token = Conjur::Authn.authenticate
|
16
|
+
api = Conjur::API.new_from_token token
|
17
|
+
userid = [ Conjur.configuration.account, "user", api.username ].join(':')
|
18
|
+
|
13
19
|
env["rack.session"][:sessionid] = sessionid
|
14
20
|
response = Rack::Response.new(env)
|
15
21
|
configuration = {
|
@@ -20,6 +26,7 @@ module Conjur
|
|
20
26
|
}
|
21
27
|
response.status = 302
|
22
28
|
response.set_cookie('conjur_configuration', value: JSON.pretty_generate(configuration), path: '/')
|
29
|
+
response.set_cookie('conjur_userid', value: userid, path: '/')
|
23
30
|
response['Location'] = "/ui"
|
24
31
|
response.finish
|
25
32
|
else
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Conjur
|
2
|
+
module WebServer
|
3
|
+
# a helper class to render HTML partials
|
4
|
+
class Renderer
|
5
|
+
def initialize root
|
6
|
+
@root = root
|
7
|
+
@files = []
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :files
|
11
|
+
|
12
|
+
def render template
|
13
|
+
ERB.new(template).result binding
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def method_missing name, *a, &b
|
19
|
+
super if !a.empty? || block_given?
|
20
|
+
|
21
|
+
# try to load fragments
|
22
|
+
path = expand_path "_#{name}.html"
|
23
|
+
super unless File.exists? path
|
24
|
+
|
25
|
+
@files << path
|
26
|
+
File.read path
|
27
|
+
end
|
28
|
+
|
29
|
+
def expand_path filename
|
30
|
+
File.expand_path(filename, @root)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/livescript/views/audit.ls
CHANGED
@@ -1,20 +1,31 @@
|
|
1
1
|
{table, div, th, tr, td, thead, tbody, section, h3, time} = React.DOM
|
2
2
|
{map, each, unique, is-type, join} = require 'prelude-ls'
|
3
3
|
|
4
|
-
|
4
|
+
compact_fields = <[ user action entities ]>
|
5
|
+
extended_fields = <[ timestamp user acting_as action entities privilege ]>
|
5
6
|
|
7
|
+
FieldsMixin = {
|
8
|
+
fields: ->
|
9
|
+
if @props.compact
|
10
|
+
compact_fields
|
11
|
+
else
|
12
|
+
extended_fields
|
13
|
+
}
|
6
14
|
|
7
15
|
AuditTableHeader = React.createClass {
|
16
|
+
mixins: [ FieldsMixin ]
|
17
|
+
|
8
18
|
display-name: \AuditTableHeader
|
19
|
+
|
9
20
|
render: ->
|
10
|
-
thead {}, (tr {}, (fields |> map -> th key: it, it.replace \_, ' '))
|
21
|
+
thead {}, (tr {}, (@fields() |> map -> th key: it, it.replace \_, ' '))
|
11
22
|
}
|
12
23
|
|
13
24
|
Timestamp = React.createClass {
|
14
25
|
display-name: \Timestamp
|
15
26
|
render: ->
|
16
27
|
ts = moment(@props.time)
|
17
|
-
time
|
28
|
+
time date-time: ts.format!, title: ts.calendar!, [ ts.from-now! ]
|
18
29
|
}
|
19
30
|
|
20
31
|
wrap-array = ->
|
@@ -23,24 +34,24 @@ wrap-array = ->
|
|
23
34
|
else
|
24
35
|
[it]
|
25
36
|
|
26
|
-
AuditEntry = React.createClass {
|
27
|
-
|
37
|
+
export AuditEntry = React.createClass {
|
38
|
+
mixins: [ FieldsMixin ]
|
28
39
|
|
29
|
-
|
30
|
-
@props with
|
31
|
-
entity: @props.resource || @props.role
|
40
|
+
display-name: \AuditEntry
|
32
41
|
|
33
42
|
transform-field: (key, value) ->
|
34
|
-
|
35
|
-
|
|
36
|
-
|
37
|
-
|
38
|
-
|
43
|
+
switch key
|
44
|
+
| \entities => [
|
45
|
+
if @props.resource? then ResourceLink data: that
|
46
|
+
if @props.role? then RoleLink id: that
|
47
|
+
]
|
48
|
+
| <[ user acting_as ]> => RoleLink {id: value} if value?
|
49
|
+
| \timestamp => Timestamp {time: value} if value?
|
50
|
+
| _ => value
|
39
51
|
|
40
52
|
render: ->
|
41
|
-
props = @transformed-props!
|
42
53
|
tr class-name: @props.action,
|
43
|
-
fields |> map ~> td key: it,
|
54
|
+
@fields() |> map ~> td key: it, ...wrap-array(@transform-field(it, @props[it]))
|
44
55
|
}
|
45
56
|
|
46
57
|
# compare events by id to prevent duplicates
|
@@ -52,19 +63,20 @@ new-event-set = ->
|
|
52
63
|
@priv.comparator(existing, item) == 0 if existing?
|
53
64
|
evts
|
54
65
|
|
55
|
-
AuditTable = React.createClass {
|
66
|
+
export AuditTable = React.createClass {
|
56
67
|
display-name: \AuditTable
|
57
68
|
|
58
69
|
get-initial-state: ->
|
59
70
|
events: new-event-set!
|
60
71
|
|
61
72
|
render: ->
|
73
|
+
compact = @props.compact
|
62
74
|
section class-name: \audit, [
|
63
75
|
h3 {}, @props.caption
|
64
76
|
table class-name: \audit-table, [
|
65
|
-
AuditTableHeader(key: \thead),
|
77
|
+
AuditTableHeader(key: \thead, compact: compact),
|
66
78
|
tbody key: \tbody,
|
67
|
-
@state.events.map -> new AuditEntry it with key: it.id
|
79
|
+
@state.events.map -> new AuditEntry it with key: it.id, compact: compact
|
68
80
|
]
|
69
81
|
]
|
70
82
|
|
@@ -119,6 +131,6 @@ export AuditBox = React.createClass {
|
|
119
131
|
things = (roles ++ resources) |> unique |> join ', '
|
120
132
|
AuditTable {
|
121
133
|
src: role-srcs ++ res-srcs
|
122
|
-
caption: "Recent
|
134
|
+
caption: "Recent Activity"
|
123
135
|
}
|
124
136
|
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<script src="/js/models/namespace.js"></script>
|
2
|
+
<script src="/js/models/resourceList.js"></script>
|
3
|
+
<script src="/js/models/policyList.js"></script>
|
4
|
+
<script src="/js/models/userList.js"></script>
|
5
|
+
<script src="/js/models/variableList.js"></script>
|
6
|
+
<script src="/js/models/record.js"></script>
|
7
|
+
<script src="/js/models/groupRecord.js"></script>
|
8
|
+
<script src="/js/models/layerRecord.js"></script>
|
9
|
+
<script src="/js/models/variableRecord.js"></script>
|
10
|
+
<script src="/js/models/hostRecord.js"></script>
|
11
|
+
<script src="/js/models/userRecord.js"></script>
|
12
|
+
<script type="text/jsx" src="/js/views/mixins/search.js"></script>
|
13
|
+
<script type="text/jsx" src="/js/views/owned.js"></script>
|
14
|
+
<script type="text/jsx" src="/js/views/audit.js"></script>
|
15
|
+
<script type="text/jsx" src="/js/views/dashboard.js"></script>
|
16
|
+
<script type="text/jsx" src="/js/views/generic.js"></script>
|
17
|
+
<script type="text/jsx" src="/js/views/namespaces.js"></script>
|
18
|
+
<script type="text/jsx" src="/js/views/users.js"></script>
|
19
|
+
<script type="text/jsx" src="/js/views/hosts.js"></script>
|
20
|
+
<script type="text/jsx" src="/js/views/host.js"></script>
|
21
|
+
<script type="text/jsx" src="/js/views/role.js"></script>
|
22
|
+
<script type="text/jsx" src="/js/views/group.js"></script>
|
23
|
+
<script type="text/jsx" src="/js/views/groups.js"></script>
|
24
|
+
<script type="text/jsx" src="/js/views/layer.js"></script>
|
25
|
+
<script type="text/jsx" src="/js/views/layers.js"></script>
|
26
|
+
<script type="text/jsx" src="/js/views/variable.js"></script>
|
27
|
+
<script type="text/jsx" src="/js/views/variables.js"></script>
|
28
|
+
<script type="text/jsx" src="/js/views/policies.js"></script>
|
29
|
+
<script type="text/jsx" src="/js/views/policy.js"></script>
|
30
|
+
<script type="text/jsx" src="/js/views/resource.js"></script>
|
31
|
+
<script type="text/jsx" src="/js/views/permissions.js"></script>
|
32
|
+
<script type="text/jsx" src="/js/views/user.js"></script>
|
33
|
+
<script type="text/jsx" src="/js/views/time.js"></script>
|
34
|
+
<script type="text/jsx" src="/js/views/navSearch.js"></script>
|
35
|
+
<script type="text/jsx" src="/js/views/searchResults.js"></script>
|
36
|
+
<script src="/js/views/audit.js"></script>
|
37
|
+
<script type="text/jsx" src="/js/main.js"></script>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
2
|
+
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
3
|
+
<!--[if lt IE 9]>
|
4
|
+
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
|
5
|
+
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
|
6
|
+
<![endif]-->
|
7
|
+
|
8
|
+
<script src="/js/lib/sorted-set.no-require.js"></script>
|
9
|
+
<script src="/js/lib/less.js"></script>
|
10
|
+
<script src="/js/lib/react-with-addons.js"></script>
|
11
|
+
<script src="/js/lib/JSXTransformer.js"></script>
|
12
|
+
<script src="http://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js"></script>
|
13
|
+
<script src="http://code.jquery.com/jquery-1.10.0.min.js"></script>
|
14
|
+
<script src="/js/lib/async.js"></script>
|
15
|
+
<script src="/js/lib/underscore-min.js"></script>
|
16
|
+
<script src="/js/lib/underscore.string.min.js"></script>
|
17
|
+
<script src="/js/lib/backbone.js"></script>
|
18
|
+
<script src="/js/lib/bootstrap.js"></script>
|
19
|
+
<script src="/js/lib/date.extensions.js"></script>
|
20
|
+
<script src="/js/lib/moment.js"></script>
|
21
|
+
<script src="/js/lib/prelude-browser-min.js"></script>
|
data/public/css/styles.less
CHANGED
@@ -154,17 +154,17 @@ dl.propertyList {
|
|
154
154
|
.role-link-icon('/images/icon-environment.png', 1.1em);
|
155
155
|
}
|
156
156
|
|
157
|
-
.host.role-link {
|
157
|
+
.host.role-link, .host.resource-link {
|
158
158
|
.role-link-icon('/images/icon-client-pc.svg');
|
159
159
|
background-position-y: -0.1em;
|
160
160
|
}
|
161
161
|
|
162
|
-
.user.role-link {
|
162
|
+
.user.role-link, .user.resource-link {
|
163
163
|
.role-link-icon('/images/icon-person.svg');
|
164
164
|
background-position-y: -0.2em;
|
165
165
|
}
|
166
166
|
|
167
|
-
.group.role-link {
|
167
|
+
.group.role-link, .group.resource-link {
|
168
168
|
.role-link-padding;
|
169
169
|
.group-icon(1.2em);
|
170
170
|
}
|
@@ -179,6 +179,25 @@ dl.propertyList {
|
|
179
179
|
margin-left: 0.5em;
|
180
180
|
}
|
181
181
|
|
182
|
+
|
183
|
+
.dashboard {
|
184
|
+
#dashboard-search {
|
185
|
+
margin: 4em auto;
|
186
|
+
}
|
187
|
+
|
188
|
+
.owned ul {
|
189
|
+
margin: 0;
|
190
|
+
padding: 0;
|
191
|
+
}
|
192
|
+
|
193
|
+
.owned .hide-all { display: none; }
|
194
|
+
|
195
|
+
.owned li {
|
196
|
+
list-style-type: none;
|
197
|
+
padding: 3pt 8pt;
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
182
201
|
.dashboard form.search, #searchResults form.search {
|
183
202
|
margin: 0px auto;
|
184
203
|
width: 300px;
|
@@ -270,7 +289,7 @@ body {
|
|
270
289
|
}
|
271
290
|
|
272
291
|
h3 {
|
273
|
-
margin:
|
292
|
+
margin: 1em 0 1em;
|
274
293
|
letter-spacing: 0.01em;
|
275
294
|
font-weight: 300;
|
276
295
|
}
|
@@ -296,12 +315,6 @@ body {
|
|
296
315
|
//
|
297
316
|
// Styleguide 1.1.
|
298
317
|
section.permissions {
|
299
|
-
&.loading {
|
300
|
-
h3:after {
|
301
|
-
content: ' — loading, please wait…';
|
302
|
-
font-weight: 300;
|
303
|
-
}
|
304
|
-
}
|
305
318
|
}
|
306
319
|
|
307
320
|
// The topmost content container.
|
@@ -0,0 +1,71 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Conjur UI</title>
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<link href='http://fonts.googleapis.com/css?family=Lato:300,400,700,300italic,400italic,700italic' rel='stylesheet' type='text/css'>
|
7
|
+
|
8
|
+
<script src="/js/lib/pace.js"></script>
|
9
|
+
<!-- Bootstrap -->
|
10
|
+
<link href="/css/bootstrap.css" rel="stylesheet">
|
11
|
+
|
12
|
+
<!-- Some styles of our own -->
|
13
|
+
<link href="/css/styles.less" rel="stylesheet/less">
|
14
|
+
|
15
|
+
<%= client_libs %>
|
16
|
+
</head>
|
17
|
+
<body>
|
18
|
+
<div class="container">
|
19
|
+
<div class="navbar navbar-default" role="navigation">
|
20
|
+
<div class="navbar-collapse collapse">
|
21
|
+
<ul class="nav navbar-nav">
|
22
|
+
<li id="nav-home" class="nav-item"><a href="/ui">Dashboard</a></li>
|
23
|
+
<li id="nav-groups" class="dropdown">
|
24
|
+
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
25
|
+
Actors <span class="caret"></span>
|
26
|
+
</a>
|
27
|
+
<ul class="dropdown-menu">
|
28
|
+
<li id="nav-users" class="nav-item"><a href="/ui/users">Users</a></li>
|
29
|
+
<li id="nav-groups" class="nav-item"><a href="/ui/groups">Groups</a></li>
|
30
|
+
<li class="divider"></li>
|
31
|
+
<li id="nav-hosts" class="nav-item"><a href="/ui/hosts">Hosts</a></li>
|
32
|
+
<li id="nav-layers" class="nav-item"><a href="/ui/layers">Layers</a></li>
|
33
|
+
</ul>
|
34
|
+
</li>
|
35
|
+
<li id="nav-assets" class="dropdown">
|
36
|
+
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
37
|
+
Assets <span class="caret"></span>
|
38
|
+
</a>
|
39
|
+
<ul class="dropdown-menu">
|
40
|
+
<li id="nav-hosts" class="nav-item"><a href="/ui/hosts">Hosts</a></li>
|
41
|
+
<li id="nav-variables" class="nav-item"><a href="/ui/variables">Variables</a></li>
|
42
|
+
<li id="nav-key-pairs" class="nav-item"><a href="/ui/key-pairs">Key Pairs</a></li>
|
43
|
+
<li id="nav-web-services" class="nav-item"><a href="/ui/webservices">Web Services</a></li>
|
44
|
+
</ul>
|
45
|
+
</li>
|
46
|
+
<li id="nav-policies" class="nav-item"><a href="/ui/policies">Policies</a></li>
|
47
|
+
<li id="nav-audit" class="nav-item"><a href="/ui/audit">Audit</a></li>
|
48
|
+
</ul>
|
49
|
+
<div class="navbar-right" id="inlineSearchContainer">
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
|
54
|
+
</div>
|
55
|
+
<div class="alert alert-danger alert-dismissable" id="flash">
|
56
|
+
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
57
|
+
<span class="text"></span>
|
58
|
+
</div>
|
59
|
+
<div id="content"></div>
|
60
|
+
<div id="bottom"></div>
|
61
|
+
</div><!-- /.container -->
|
62
|
+
|
63
|
+
|
64
|
+
<div id="modal">
|
65
|
+
|
66
|
+
</div>
|
67
|
+
|
68
|
+
|
69
|
+
<%= client_code %>
|
70
|
+
</body>
|
71
|
+
</html>
|