conjur-asset-ui-api 1.1.1 → 1.2.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 +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>
|