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.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/.git-hooks/pre_commit/ensure_livescript_compiled.rb +31 -0
  3. data/.git-hooks/pre_commit/trailing_whitespace.rb +26 -0
  4. data/.gitignore +20 -0
  5. data/.overcommit.yml +5 -0
  6. data/.project +18 -0
  7. data/Gemfile +8 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +41 -0
  10. data/Rakefile +1 -0
  11. data/compile_ls +2 -0
  12. data/conjur-asset-ui.gemspec +36 -0
  13. data/lib/conjur-asset-ui-version.rb +7 -0
  14. data/lib/conjur-asset-ui.rb +7 -0
  15. data/lib/conjur/audit/follower.rb +63 -0
  16. data/lib/conjur/audit/humanizer.rb +53 -0
  17. data/lib/conjur/audit/tableizer.rb +55 -0
  18. data/lib/conjur/command/ui.rb +38 -0
  19. data/lib/conjur/webserver/api_proxy.rb +94 -0
  20. data/lib/conjur/webserver/audit_stream.rb +92 -0
  21. data/lib/conjur/webserver/authorize.rb +28 -0
  22. data/lib/conjur/webserver/conjur_info.rb +33 -0
  23. data/lib/conjur/webserver/home.rb +36 -0
  24. data/lib/conjur/webserver/login.rb +50 -0
  25. data/lib/conjur/webserver/server.rb +111 -0
  26. data/livescript/views/audit.ls +124 -0
  27. data/public/css/bootstrap.css +7 -0
  28. data/public/css/styles.less +400 -0
  29. data/public/fonts/glyphicons-halflings-regular.eot +0 -0
  30. data/public/fonts/glyphicons-halflings-regular.svg +229 -0
  31. data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  32. data/public/fonts/glyphicons-halflings-regular.woff +0 -0
  33. data/public/images/conjur-logo.svg +26 -0
  34. data/public/images/icon-client-pc.svg +12 -0
  35. data/public/images/icon-environment.png +0 -0
  36. data/public/images/icon-person.svg +12 -0
  37. data/public/images/icon-service-dots.svg +13 -0
  38. data/public/images/icon-variable.png +0 -0
  39. data/public/index.html +121 -0
  40. data/public/js/lib/JSXTransformer.js +10862 -0
  41. data/public/js/lib/async.js +958 -0
  42. data/public/js/lib/backbone.js +2 -0
  43. data/public/js/lib/bootstrap.js +6 -0
  44. data/public/js/lib/date.extensions.js +141 -0
  45. data/public/js/lib/less.js +16 -0
  46. data/public/js/lib/moment.js +7768 -0
  47. data/public/js/lib/pace.js +2 -0
  48. data/public/js/lib/prelude-browser-min.js +1 -0
  49. data/public/js/lib/react-with-addons.js +15505 -0
  50. data/public/js/lib/react.js +14469 -0
  51. data/public/js/lib/sorted-set.no-require.js +1170 -0
  52. data/public/js/lib/sorted-set.no-require.js.txt +6 -0
  53. data/public/js/lib/underscore-min.js +6 -0
  54. data/public/js/lib/underscore.string.min.js +1 -0
  55. data/public/js/main.js +353 -0
  56. data/public/js/models/namespace.js +6 -0
  57. data/public/js/models/policyList.js +10 -0
  58. data/public/js/models/record.js +26 -0
  59. data/public/js/models/resourceList.js +61 -0
  60. data/public/js/models/userList.js +16 -0
  61. data/public/js/models/variableList.js +12 -0
  62. data/public/js/views/audit.js +191 -0
  63. data/public/js/views/dashboard.js +35 -0
  64. data/public/js/views/generic.js +42 -0
  65. data/public/js/views/group.js +32 -0
  66. data/public/js/views/groups.js +18 -0
  67. data/public/js/views/host.js +40 -0
  68. data/public/js/views/hosts.js +18 -0
  69. data/public/js/views/layer.js +63 -0
  70. data/public/js/views/layers.js +18 -0
  71. data/public/js/views/mixins/search.js +9 -0
  72. data/public/js/views/namespaces.js +40 -0
  73. data/public/js/views/navSearch.js +16 -0
  74. data/public/js/views/permissions.js +91 -0
  75. data/public/js/views/policies.js +17 -0
  76. data/public/js/views/policy.js +23 -0
  77. data/public/js/views/resource.js +23 -0
  78. data/public/js/views/role.js +18 -0
  79. data/public/js/views/searchResults.js +146 -0
  80. data/public/js/views/time.js +14 -0
  81. data/public/js/views/user.js +22 -0
  82. data/public/js/views/users.js +18 -0
  83. data/public/js/views/variable.js +41 -0
  84. data/public/js/views/variables.js +18 -0
  85. data/vendor/prelude-ls/.gitignore +2 -0
  86. data/vendor/prelude-ls/.travis.yml +3 -0
  87. data/vendor/prelude-ls/CHANGELOG.md +81 -0
  88. data/vendor/prelude-ls/LICENSE +22 -0
  89. data/vendor/prelude-ls/Makefile +50 -0
  90. data/vendor/prelude-ls/README.md +15 -0
  91. data/vendor/prelude-ls/browser/prelude-browser-min.js +1 -0
  92. data/vendor/prelude-ls/browser/prelude-browser.js +1172 -0
  93. data/vendor/prelude-ls/lib/Func.js +40 -0
  94. data/vendor/prelude-ls/lib/List.js +602 -0
  95. data/vendor/prelude-ls/lib/Num.js +129 -0
  96. data/vendor/prelude-ls/lib/Obj.js +153 -0
  97. data/vendor/prelude-ls/lib/Str.js +68 -0
  98. data/vendor/prelude-ls/lib/index.js +164 -0
  99. data/vendor/prelude-ls/package.json +50 -0
  100. data/vendor/prelude-ls/package.ls +46 -0
  101. data/vendor/prelude-ls/src/Func.ls +17 -0
  102. data/vendor/prelude-ls/src/List.ls +299 -0
  103. data/vendor/prelude-ls/src/Num.ls +83 -0
  104. data/vendor/prelude-ls/src/Obj.ls +61 -0
  105. data/vendor/prelude-ls/src/Str.ls +32 -0
  106. data/vendor/prelude-ls/src/index.ls +56 -0
  107. data/vendor/prelude-ls/test/Func.ls +36 -0
  108. data/vendor/prelude-ls/test/List.ls +751 -0
  109. data/vendor/prelude-ls/test/Num.ls +258 -0
  110. data/vendor/prelude-ls/test/Obj.ls +145 -0
  111. data/vendor/prelude-ls/test/Prelude.ls +49 -0
  112. data/vendor/prelude-ls/test/Str.ls +208 -0
  113. data/vendor/prelude-ls/test/browser.html +5 -0
  114. metadata +328 -0
@@ -0,0 +1,94 @@
1
+ module Conjur
2
+ module WebServer
3
+ require 'rack/streaming_proxy'
4
+
5
+ class APIProxy < Rack::StreamingProxy::Proxy
6
+
7
+ class Request < Rack::StreamingProxy::Request
8
+ def initialize env
9
+ path = env["PATH_INFO"]
10
+
11
+ path =~ /^\/([^\/]+)(.*)/
12
+ app = $1
13
+ path_remainder = $2
14
+
15
+ new_url = case app
16
+ when 'authn', 'authz', 'audit', 'pubkeys'
17
+ [ Conjur.configuration.send("#{app}_url"), path_remainder ].join
18
+ else
19
+ [ Conjur.configuration.send("core_url"), path ].join
20
+ end
21
+ if query = env["QUERY_STRING"]
22
+ new_url = [ new_url, query ].join('?')
23
+ end
24
+
25
+ super new_url.to_s, Rack::Request.new(env)
26
+ end
27
+ end
28
+
29
+ Rack::StreamingProxy::Proxy.set_default_configuration
30
+
31
+ def initialize
32
+ super nil
33
+ end
34
+
35
+ def call env
36
+ request = Request.new(env)
37
+ request.http_request['Authorization'] = authorization_header
38
+ response = Rack::StreamingProxy::Session.new(request).start
39
+ rewrite_response(env, response.status, response.headers, response)
40
+ end
41
+
42
+ def rewrite_response(*args)
43
+ env, status, headers, body = args
44
+
45
+ source_request = Rack::Request.new(env)
46
+
47
+ headers = Hash[*headers.flat_map { |k, v| [capitalize_header(k), v] }]
48
+ headers.delete 'Transfer-Encoding' # let Puma handle chunking
49
+
50
+ # Rewrite location
51
+ if location = headers["Location"]
52
+ headers["Location"] = location.gsub(Conjur.configuration.service_url, "http://#{source_request.host}:#{source_request.port}")
53
+ end
54
+
55
+ [ status, headers, body ]
56
+ end
57
+
58
+ protected
59
+
60
+ def authorization_header
61
+ require 'conjur/authn'
62
+ require 'base64'
63
+ token = Conjur::Authn.authenticate
64
+ "Token token=\"#{Base64.strict_encode64(token.to_json)}\""
65
+ end
66
+
67
+ def perform_request(env)
68
+ triplet = super(env)
69
+ [ env ] + triplet
70
+ end
71
+
72
+ private
73
+
74
+ def capitalize_header hdr
75
+ hdr.split('-').map(&:capitalize).join('-')
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ # Rack::StreamingProxy by default doesn't handle closing, which leads to stale
82
+ # (but still running) connections. Handle the close by quitting the child
83
+ # (it will close connection with upstream automatically).
84
+ class Rack::StreamingProxy::Response
85
+ def initialize piper
86
+ @piper = piper
87
+ @client_http_version = '1.0'
88
+ receive
89
+ end
90
+
91
+ def close
92
+ @piper.signal :QUIT
93
+ end
94
+ end
@@ -0,0 +1,92 @@
1
+ require 'eventmachine'
2
+ require 'conjur/audit/humanizer'
3
+ require 'conjur/audit/tableizer'
4
+
5
+ module Conjur
6
+ module WebServer
7
+ class AuditStream
8
+ include Conjur::Audit::Humanizer
9
+ include Conjur::Audit::Tableizer
10
+
11
+ class Body
12
+ include EM::Deferrable
13
+ def write chunk
14
+ @callable.call chunk
15
+ end
16
+ def each &block
17
+ @callable = block
18
+ end
19
+ end
20
+
21
+ HEADERS = {
22
+ "Content-Type" => "text/event-stream",
23
+ "Connection" => "keepalive",
24
+ "Cache-Control" => "no-cache, no-store"
25
+ }
26
+
27
+ def call env
28
+ body = Body.new
29
+ stream_events(env) do |events|
30
+ write_events body, events
31
+ end
32
+ [200, HEADERS, body]
33
+ end
34
+
35
+ def stream_events env, &block
36
+ # This could be a lot more "EventMachineish" by using for example
37
+ # EM::HttpRequest, but putting it in the thread pool should be
38
+ # good enough for our purposes.
39
+ EM.defer do
40
+ follower = Conjur::Audit::Follower.new{|opts| fetch_events(env, opts)}
41
+ follower.filter{|e| self_event?(env, e)} unless show_self_events?(env)
42
+ follower.follow &block
43
+ end
44
+ end
45
+
46
+ # Returns true if this looks like a permission check performed by the
47
+ # audit service
48
+ def self_event? env, e
49
+ e['action'] == 'check' && e['asset'] == 'resource' && e['conjur_role'] == e['role'] && e['role'] == env['conjur.roleid']
50
+ end
51
+
52
+ def show_self_events? env
53
+ !!Rack::Request.new(env).params['self']
54
+ end
55
+
56
+ # Returns [kind, id]
57
+ def parse_path env
58
+ path = env["SCRIPT_NAME"] + env["PATH_INFO"]
59
+ %r{^/api/audit/stream/(.*?)(?:/(.*))?$} =~ path
60
+ [$1, $2]
61
+ end
62
+
63
+ def fetch_events env, options
64
+ kind, id = parse_path env
65
+ args = if kind == 'role' && id.nil?
66
+ [:audit_current_role, options]
67
+ else
68
+ [:"audit_#{kind}", id, options]
69
+ end
70
+ format = Rack::Request.new(env).params['format'] || 'string'
71
+ format_method = case format
72
+ when 'table'
73
+ :tableize
74
+ else
75
+ :humanize
76
+ end
77
+ api.send(*args).each {|e| send(format_method, e)}
78
+ end
79
+
80
+ def write_events body, events
81
+ events.each do |e|
82
+ body.write "id: #{e['event_id']}\n"
83
+ body.write "data: #{JSON.generate e}\n\n"
84
+ end
85
+ end
86
+
87
+ def api
88
+ Conjur::API.new_from_token Conjur::Authn.authenticate
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,28 @@
1
+ module Conjur
2
+ module WebServer
3
+ # Verifies that the request contains the authorization token, and then strips it.
4
+ class Authorize
5
+ attr_reader :app, :sessionid
6
+
7
+ def initialize(app, sessionid)
8
+ @app = app
9
+ @sessionid = sessionid
10
+ end
11
+
12
+ def call(env)
13
+ if token_valid?(env)
14
+ @app.call env
15
+ else
16
+ [403, {}, ["Authorization is missing or invalid"]]
17
+ end
18
+ end
19
+
20
+ protected
21
+
22
+ def token_valid?(env)
23
+ request = Rack::Request.new(env)
24
+ request.session[:sessionid] == sessionid
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ module Conjur
2
+ module WebServer
3
+ # Middleware that adds some conjur info to the rack environment
4
+ class ConjurInfo
5
+ def initialize app
6
+ @app = app
7
+ end
8
+
9
+ def call env
10
+ update_env env
11
+ @app.call env
12
+ end
13
+
14
+ def update_env env
15
+ PROPERTIES.each{|name| env["conjur.#{name}"] = send(name)}
16
+ end
17
+
18
+ PROPERTIES = %w(roleid account stack)
19
+
20
+ def roleid
21
+ "#{account}:user:#{Conjur::Authn.get_credentials[0]}"
22
+ end
23
+
24
+ def account
25
+ Conjur.account
26
+ end
27
+
28
+ def stack
29
+ Conjur.stack
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,36 @@
1
+ require 'time'
2
+ require 'rack/utils'
3
+ require 'rack/mime'
4
+
5
+ module Conjur
6
+ module WebServer
7
+ class Home
8
+ F = ::File
9
+
10
+ def initialize(root)
11
+ @root = root
12
+ end
13
+
14
+ # From Rack::File
15
+ def call(env)
16
+ path = File.expand_path("index.html", @root)
17
+
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
+ return [304, {}, []] if env['HTTP_IF_MODIFIED_SINCE'] == last_modified
23
+
24
+ size = F.size?(path) || Rack::Utils.bytesize(F.read(path))
25
+
26
+ headers = {
27
+ "Last-Modified" => last_modified,
28
+ "Content-Type" => "text/html",
29
+ "Content-Length" => size.to_s
30
+ }
31
+
32
+ [ 200, headers, env["REQUEST_METHOD"] == "HEAD" ? [] : [ F.read(path) ] ]
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,50 @@
1
+
2
+ module Conjur
3
+ module WebServer
4
+ class Login
5
+ attr_reader :sessionid
6
+
7
+ def initialize(sessionid)
8
+ @sessionid = sessionid
9
+ end
10
+
11
+ def call(env)
12
+ if sessionid = token_valid?(env)
13
+ env["rack.session"][:sessionid] = sessionid
14
+ response = Rack::Response.new(env)
15
+ configuration = {
16
+ account: Conjur.configuration.account,
17
+ stack: Conjur.configuration.stack,
18
+ appliance_url: Conjur.configuration.appliance_url,
19
+ login: Conjur::Authn.get_credentials[0]
20
+ }
21
+ response.status = 302
22
+ response.set_cookie('conjur_configuration', value: JSON.pretty_generate(configuration), path: '/')
23
+ response['Location'] = "/ui"
24
+ response.finish
25
+ else
26
+ [ 403, {}, ["Authorization is missing or invalid"] ]
27
+ end
28
+ end
29
+
30
+ protected
31
+
32
+ def token_valid?(env)
33
+ token = extract_token(env)
34
+ if token == sessionid
35
+ sessionid
36
+ else
37
+ nil
38
+ end
39
+ end
40
+
41
+ def extract_token(env)
42
+ require 'cgi'
43
+ require 'uri'
44
+ query = URI.parse(env['REQUEST_URI']).query
45
+ query && ( sessionid = CGI.parse(query)['sessionid'] ) && sessionid[0]
46
+ end
47
+ end
48
+ end
49
+ end
50
+
@@ -0,0 +1,111 @@
1
+ #
2
+ # Copyright (C) 2013 Conjur Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ # this software and associated documentation files (the "Software"), to deal in
6
+ # the Software without restriction, including without limitation the rights to
7
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
+ # the Software, and to permit persons to whom the Software is furnished to do so,
9
+ # subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+ #
21
+
22
+ module Conjur
23
+ module WebServer
24
+ # Launch a web server which serves local files and proxies to the remote Conjur API.
25
+ class Server
26
+ def start(root)
27
+ require 'rack'
28
+ require 'conjur/webserver/login'
29
+ require 'conjur/webserver/authorize'
30
+ require 'conjur/webserver/api_proxy'
31
+ require 'conjur/webserver/home'
32
+ require 'conjur/webserver/conjur_info'
33
+ require 'pry'
34
+
35
+ sessionid = self.sessionid
36
+ cookie_options = {
37
+ secret: SecureRandom.hex(32),
38
+ expire_after: 24*60*60
39
+ }
40
+
41
+ api_stack = [
42
+ [Rack::Session::Cookie, cookie_options],
43
+ #[Conjur::WebServer::Authorize, sessionid],
44
+ [Conjur::WebServer::ConjurInfo]
45
+ ]
46
+
47
+ app = Rack::Builder.app do
48
+ map "/login" do
49
+ use Rack::Session::Cookie, cookie_options
50
+ run Conjur::WebServer::Login.new sessionid
51
+ end
52
+ map "/api" do
53
+ api_stack.each{|args| use *args}
54
+ run Conjur::WebServer::APIProxy.new
55
+ end
56
+ %w(js css fonts images).each do |path|
57
+ map "/#{path}" do
58
+ run Rack::File.new(File.join(root, path), 'Cache-Control' => 'max-age=0')
59
+ end
60
+ end
61
+ map "/ui" do
62
+ run Conjur::WebServer::Home.new(root)
63
+ end
64
+ end
65
+ options = {
66
+ app: app,
67
+ Port: port,
68
+ Threads: '0:64',
69
+ Verbose: true
70
+ }
71
+
72
+ # this vivifies the env for correct url settings
73
+ # otherwise puma sets it to development and
74
+ # confusion ensues
75
+ Conjur.configuration.env
76
+
77
+ Rack::Server.start(options)
78
+ end
79
+
80
+ def open
81
+ require 'launchy'
82
+ url = "http://localhost:#{port}/login?sessionid=#{sessionid}"
83
+ # as launchy sometimes silently fails, we need human-friendly failover
84
+ $stderr.puts "UI should be available now at #{url}"
85
+ Launchy.open(url)
86
+ end
87
+
88
+ protected
89
+
90
+ def port
91
+ @port ||= find_available_port
92
+ end
93
+
94
+ DEFAULT_PORT = 42_289
95
+
96
+ def find_available_port
97
+ begin
98
+ server = TCPServer.new('127.0.0.1', 0)
99
+ server.addr[1]
100
+ ensure
101
+ server.close if server
102
+ end
103
+ end
104
+
105
+ def sessionid
106
+ require 'securerandom'
107
+ @sessionid ||= SecureRandom.hex(32)
108
+ end
109
+ end
110
+ end
111
+ end