conjur-asset-ui-api 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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