etna 0.1.43 → 0.1.46

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/bin/etna +12 -1
  3. data/etna.completion +36788 -1
  4. data/lib/commands.rb +118 -0
  5. data/lib/etna/application.rb +13 -16
  6. data/lib/etna/auth.rb +37 -14
  7. data/lib/etna/clients/janus/client.rb +17 -18
  8. data/lib/etna/clients/janus/models.rb +52 -0
  9. data/lib/etna/clients/magma/client.rb +41 -0
  10. data/lib/etna/clients/magma/formatting/models_csv.rb +14 -4
  11. data/lib/etna/clients/magma/models.rb +19 -8
  12. data/lib/etna/clients/magma/workflows/create_project_workflow.rb +1 -1
  13. data/lib/etna/clients/magma/workflows/json_validators.rb +5 -5
  14. data/lib/etna/clients/magma/workflows/model_synchronization_workflow.rb +9 -9
  15. data/lib/etna/clients/metis/client.rb +7 -2
  16. data/lib/etna/clients/metis/models.rb +6 -3
  17. data/lib/etna/clients/metis/workflows/metis_download_workflow.rb +1 -1
  18. data/lib/etna/clients/metis/workflows/sync_metis_data_workflow.rb +23 -0
  19. data/lib/etna/clients/metis/workflows/walk_metis_diff_workflow.rb +95 -0
  20. data/lib/etna/clients/metis/workflows/walk_metis_workflow.rb +31 -0
  21. data/lib/etna/clients/metis/workflows.rb +2 -0
  22. data/lib/etna/controller.rb +48 -1
  23. data/lib/etna/cwl.rb +8 -0
  24. data/lib/etna/environment_variables.rb +50 -0
  25. data/lib/etna/filesystem.rb +2 -0
  26. data/lib/etna/injection.rb +57 -0
  27. data/lib/etna/janus_utils.rb +55 -0
  28. data/lib/etna/permissions.rb +104 -0
  29. data/lib/etna/redirect.rb +36 -0
  30. data/lib/etna/route.rb +54 -5
  31. data/lib/etna/server.rb +13 -1
  32. data/lib/etna/spec/auth.rb +11 -0
  33. data/lib/etna/spec/vcr.rb +28 -3
  34. data/lib/etna/test_auth.rb +25 -1
  35. data/lib/etna/user.rb +4 -26
  36. data/lib/etna.rb +4 -0
  37. metadata +13 -6
data/lib/etna/route.rb CHANGED
@@ -15,6 +15,7 @@ module Etna
15
15
  @block = block
16
16
  @match_ext = options[:match_ext]
17
17
  @log_redact_keys = options[:log_redact_keys]
18
+ @dont_log = options[:dont_log]
18
19
  end
19
20
 
20
21
  def to_hash
@@ -37,11 +38,11 @@ module Etna
37
38
 
38
39
  UNSAFE=/[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,]/
39
40
 
40
- def self.path(route, params=nil)
41
+ def self.path(route, params=nil, &block)
41
42
  if params
42
43
  PARAM_TYPES.reduce(route) do |path,pat|
43
44
  path.gsub(pat) do
44
- URI.encode( params[$1.to_sym], UNSAFE)
45
+ params[$1.to_sym].split('/').map { |c| block_given? ? yield(c) : URI.encode_www_form_component(c) }.join('/')
45
46
  end
46
47
  end
47
48
  else
@@ -49,8 +50,8 @@ module Etna
49
50
  end
50
51
  end
51
52
 
52
- def path(params=nil)
53
- self.class.path(@route, params)
53
+ def path(params=nil, &block)
54
+ self.class.path(@route, params, &block)
54
55
  end
55
56
 
56
57
  def parts
@@ -123,10 +124,19 @@ module Etna
123
124
  update_params(request)
124
125
 
125
126
  unless authorized?(request)
127
+ if cc_available?(request)
128
+ if request.content_type == 'application/json'
129
+ return [ 403, { 'Content-Type' => 'application/json' }, [ { error: 'You are forbidden from performing this action, but you can visit the project home page and request access.' }.to_json ] ]
130
+ else
131
+ return cc_redirect(request)
132
+ end
133
+ end
134
+
126
135
  return [ 403, { 'Content-Type' => 'application/json' }, [ { error: 'You are forbidden from performing this action.' }.to_json ] ]
127
136
  end
128
137
 
129
138
  request.env['etna.redact_keys'] = @log_redact_keys
139
+ request.env['etna.dont_log'] = @dont_log
130
140
 
131
141
  if @action
132
142
  controller, action = @action.split('#')
@@ -154,8 +164,29 @@ module Etna
154
164
  @auth && @auth[:ignore_janus]
155
165
  end
156
166
 
167
+ def has_user_constraint?(constraint)
168
+ @auth && @auth[:user] && @auth[:user][constraint]
169
+ end
170
+
157
171
  private
158
172
 
173
+ def janus
174
+ @janus ||= Etna::JanusUtils.new
175
+ end
176
+
177
+ # If the application asks for a code of conduct redirect
178
+ def cc_redirect(request, msg = 'You are unauthorized')
179
+ return [ 401, { 'Content-Type' => 'text/html' }, [msg] ] unless application.config(:auth_redirect)
180
+
181
+ params = request.env['rack.request.params']
182
+
183
+ uri = URI(
184
+ application.config(:auth_redirect).chomp('/') + "/#{params[:project_name]}/cc"
185
+ )
186
+ uri.query = URI.encode_www_form(refer: request.url)
187
+ Etna::Redirect(request).to(uri.to_s)
188
+ end
189
+
159
190
  def application
160
191
  @application ||= Etna::Application.instance
161
192
  end
@@ -185,6 +216,24 @@ module Etna
185
216
  end
186
217
  end
187
218
 
219
+ def cc_available?(request)
220
+ user = request.env['etna.user']
221
+
222
+ return false unless user
223
+
224
+ params = request.env['rack.request.params']
225
+
226
+ return false unless params[:project_name]
227
+
228
+ # Only check for a CC if the user does not currently have permissions
229
+ # for the project
230
+ return false if user.permissions.keys.include?(params[:project_name])
231
+
232
+ !janus.community_projects(user.token).select do |project|
233
+ project.project_name == params[:project_name]
234
+ end.first.nil?
235
+ end
236
+
188
237
  def hmac_authorized?(request)
189
238
  # either there is no hmac requirement, or we have a valid hmac
190
239
  !@auth[:hmac] || request.env['etna.hmac']&.valid?
@@ -207,7 +256,7 @@ module Etna
207
256
  Hash[
208
257
  match.names.map(&:to_sym).zip(
209
258
  match.captures.map do |capture|
210
- URI.decode(capture)
259
+ capture.split('/').map {|c| URI.decode_www_form_component(c) }.join('/')
211
260
  end
212
261
  )
213
262
  ]
data/lib/etna/server.rb CHANGED
@@ -3,7 +3,19 @@ module Etna
3
3
  class Server
4
4
  class << self
5
5
  def route(method, path, options={}, &block)
6
- @routes ||= []
6
+ # For healthchecks, set up servers
7
+ # with an OPTIONS route on /, with noauth
8
+ @routes ||= [
9
+ Etna::Route.new(
10
+ 'OPTIONS',
11
+ '/',
12
+ {
13
+ auth: {
14
+ noauth: true
15
+ }
16
+ }
17
+ )
18
+ ]
7
19
 
8
20
  @routes << Etna::Route.new(
9
21
  method,
@@ -18,11 +18,22 @@ module Etna::Spec
18
18
  },
19
19
  non_user: {
20
20
  email: 'nessus@centaurs.org', name: 'Nessus', perm: ''
21
+ },
22
+ guest: {
23
+ email: 'sinon@troy.org', name: 'Sinon', perm: 'g:labors'
21
24
  }
22
25
  }
23
26
 
24
27
  def auth_header(user_type)
25
28
  header(*Etna::TestAuth.token_header(AUTH_USERS[user_type]))
26
29
  end
30
+
31
+ def below_admin_roles
32
+ [:editor, :viewer, :guest]
33
+ end
34
+
35
+ def below_editor_roles
36
+ [:viewer, :guest]
37
+ end
27
38
  end
28
39
  end
data/lib/etna/spec/vcr.rb CHANGED
@@ -1,9 +1,27 @@
1
+ require 'cgi'
1
2
  require 'webmock/rspec'
2
3
  require 'vcr'
3
4
  require 'openssl'
4
5
  require 'digest/sha2'
5
6
  require 'base64'
6
7
 
8
+ def clean_query(json_or_string)
9
+ if json_or_string.is_a?(Hash) && json_or_string.include?('upload_path')
10
+ json_or_string['upload_path'] = clean_query(json_or_string['upload_path'])
11
+ json_or_string
12
+ elsif json_or_string.is_a?(String)
13
+ uri = URI(json_or_string)
14
+
15
+ if uri.query&.include?('X-Etna-Signature')
16
+ uri.query = 'etna-signature'
17
+ end
18
+
19
+ uri.to_s
20
+ else
21
+ json_or_string
22
+ end
23
+ end
24
+
7
25
  def setup_base_vcr(spec_helper_dir, server: nil, application: nil)
8
26
  VCR.configure do |c|
9
27
  c.hook_into :webmock
@@ -30,6 +48,10 @@ def setup_base_vcr(spec_helper_dir, server: nil, application: nil)
30
48
  end
31
49
  end
32
50
 
51
+ c.register_request_matcher :try_uri do |request_1, request_2|
52
+ clean_query(request_1.uri) == clean_query(request_2.uri)
53
+ end
54
+
33
55
  c.register_request_matcher :try_body do |request_1, request_2|
34
56
  if request_1.headers['Content-Type'].first =~ /application\/json/
35
57
  if request_2.headers['Content-Type'].first =~ /application\/json/
@@ -40,7 +62,7 @@ def setup_base_vcr(spec_helper_dir, server: nil, application: nil)
40
62
  JSON.parse(request_2.body) rescue 'not-json'
41
63
  end
42
64
 
43
- request_1_json == request_2_json
65
+ clean_query(request_1_json) == clean_query(request_2_json)
44
66
  else
45
67
  false
46
68
  end
@@ -49,7 +71,9 @@ def setup_base_vcr(spec_helper_dir, server: nil, application: nil)
49
71
  end
50
72
  end
51
73
 
52
- # c.debug_logger = File.open('log/vcr_debug.log', 'w')
74
+ if File.exists?('log')
75
+ c.debug_logger = File.open('log/vcr_debug.log', 'w')
76
+ end
53
77
 
54
78
  c.default_cassette_options = {
55
79
  serialize_with: :compressed,
@@ -58,7 +82,7 @@ def setup_base_vcr(spec_helper_dir, server: nil, application: nil)
58
82
  else
59
83
  ENV['RERECORD'] ? :all : :once
60
84
  end,
61
- match_requests_on: [:method, :uri, :try_body, :verify_uri_route]
85
+ match_requests_on: [:method, :try_uri, :try_body, :verify_uri_route]
62
86
  }
63
87
 
64
88
  # Filter the authorization headers of any request by replacing any occurrence of that request's
@@ -111,6 +135,7 @@ def setup_base_vcr(spec_helper_dir, server: nil, application: nil)
111
135
  end
112
136
 
113
137
  def prepare_vcr_secret
138
+ p ENV
114
139
  secret = ENV["CI_SECRET"]
115
140
 
116
141
  if (secret.nil? || secret.empty?) && ENV['IS_CI'] != '1'
@@ -31,18 +31,42 @@ module Etna
31
31
  end.to_h
32
32
  end
33
33
 
34
+ def update_payload(payload, token, request)
35
+ route = server.find_route(request)
36
+
37
+ payload = payload.map{|k,v| [k.to_sym, v]}.to_h
38
+
39
+ return payload unless route
40
+
41
+ begin
42
+ permissions = Etna::Permissions.from_encoded_permissions(payload[:perm])
43
+
44
+ # Skip making an actual call to Janus. This behavior is tested in auth_spec
45
+
46
+ payload[:perm] = permissions.to_string
47
+ end if (!route.ignore_janus? && route.has_user_constraint?(:can_view?))
48
+
49
+ payload
50
+ end
51
+
34
52
  def approve_user(request)
35
53
  token = auth(request,:etna)
36
54
 
37
55
  return false unless token
38
56
 
57
+ # Useful for testing certain behavior
58
+ params = request.env["rack.request.params"]
59
+
39
60
  # Here we simply base64-encode our user hash and pass it through
40
61
  # In order to behave more like "real" tokens, we expect the user hash to be
41
62
  # in index 1 after splitting by ".".
42
63
  # We do this to support Metis client tests, we pass in tokens with multiple "."-separated parts, so
43
64
  # have to account for that.
44
65
  payload = JSON.parse(Base64.decode64(token.split('.')[1]))
45
- request.env['etna.user'] = Etna::User.new(payload.map{|k,v| [k.to_sym, v]}.to_h, token)
66
+ request.env['etna.user'] = Etna::User.new(
67
+ update_payload(payload, token, request),
68
+ token
69
+ ) unless !!params[:do_not_set_user]
46
70
  end
47
71
 
48
72
  def approve_hmac(request)
data/lib/etna/user.rb CHANGED
@@ -1,11 +1,7 @@
1
+ require_relative './permissions'
2
+
1
3
  module Etna
2
4
  class User
3
- ROLE_NAMES = {
4
- 'A' => :admin,
5
- 'E' => :editor,
6
- 'V' => :viewer
7
- }
8
-
9
5
  def initialize params, token=nil
10
6
  @name, @email, @encoded_permissions, encoded_flags, @task = params.values_at(:name, :email, :perm, :flags, :task)
11
7
 
@@ -21,19 +17,7 @@ module Etna
21
17
  end
22
18
 
23
19
  def permissions
24
- @permissions ||= @encoded_permissions.split(/\;/).map do |roles|
25
- role, projects = roles.split(/:/)
26
-
27
- projects.split(/\,/).reduce([]) do |perms,project_name|
28
- perms.push([
29
- project_name,
30
- {
31
- role: ROLE_NAMES[role.upcase],
32
- restricted: role == role.upcase
33
- }
34
- ])
35
- end
36
- end.inject([],:+).to_h
20
+ @permissions ||= Etna::Permissions.from_encoded_permissions(@encoded_permissions).to_hash
37
21
  end
38
22
 
39
23
  def has_flag?(flag)
@@ -44,12 +28,6 @@ module Etna
44
28
  permissions.keys
45
29
  end
46
30
 
47
- ROLE_MATCH = {
48
- admin: /[Aa]/,
49
- editor: /[Ee]/,
50
- viewer: /[Vv]/,
51
- restricted: /[AEV]/,
52
- }
53
31
  def has_any_role?(project, *roles)
54
32
  perm = permissions[project.to_s]
55
33
 
@@ -75,7 +53,7 @@ module Etna
75
53
  end
76
54
 
77
55
  def can_view? project
78
- is_superviewer? || has_any_role?(project, :admin, :editor, :viewer)
56
+ is_superviewer? || has_any_role?(project, :admin, :editor, :viewer, :guest)
79
57
  end
80
58
 
81
59
  # superusers - administrators of the Administration group - cannot
data/lib/etna.rb CHANGED
@@ -1,4 +1,7 @@
1
1
  require 'json'
2
+ require_relative './etna/redirect'
3
+ require_relative './etna/environment_variables'
4
+ require_relative './etna/injection'
2
5
  require_relative './etna/ext'
3
6
  require_relative './etna/logger'
4
7
  require_relative './etna/server'
@@ -24,6 +27,7 @@ require_relative './etna/cwl'
24
27
  require_relative './etna/metrics'
25
28
  require_relative './etna/remote'
26
29
  require_relative './etna/synchronize_db'
30
+ require_relative './etna/janus_utils'
27
31
 
28
32
  class EtnaApp
29
33
  include Etna::Application
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: etna
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.43
4
+ version: 0.1.46
5
5
  platform: ruby
6
6
  authors:
7
7
  - Saurabh Asthana
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-28 00:00:00.000000000 Z
11
+ date: 2022-08-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -56,16 +56,16 @@ dependencies:
56
56
  name: multipart-post
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: 2.1.1
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: 2.1.1
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rollbar
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -177,6 +177,8 @@ files:
177
177
  - lib/etna/clients/metis/workflows/metis_download_workflow.rb
178
178
  - lib/etna/clients/metis/workflows/metis_upload_workflow.rb
179
179
  - lib/etna/clients/metis/workflows/sync_metis_data_workflow.rb
180
+ - lib/etna/clients/metis/workflows/walk_metis_diff_workflow.rb
181
+ - lib/etna/clients/metis/workflows/walk_metis_workflow.rb
180
182
  - lib/etna/clients/polyphemus.rb
181
183
  - lib/etna/clients/polyphemus/client.rb
182
184
  - lib/etna/clients/polyphemus/models.rb
@@ -190,17 +192,22 @@ files:
190
192
  - lib/etna/describe_routes.rb
191
193
  - lib/etna/directed_graph.rb
192
194
  - lib/etna/environment_scoped.rb
195
+ - lib/etna/environment_variables.rb
193
196
  - lib/etna/errors.rb
194
197
  - lib/etna/ext.rb
195
198
  - lib/etna/filesystem.rb
196
199
  - lib/etna/formatting.rb
197
200
  - lib/etna/generate_autocompletion_script.rb
198
201
  - lib/etna/hmac.rb
202
+ - lib/etna/injection.rb
203
+ - lib/etna/janus_utils.rb
199
204
  - lib/etna/json_serializable_struct.rb
200
205
  - lib/etna/logger.rb
201
206
  - lib/etna/metrics.rb
202
207
  - lib/etna/multipart_serializable_nested_hash.rb
203
208
  - lib/etna/parse_body.rb
209
+ - lib/etna/permissions.rb
210
+ - lib/etna/redirect.rb
204
211
  - lib/etna/remote.rb
205
212
  - lib/etna/route.rb
206
213
  - lib/etna/server.rb