skull_island 1.2.7 → 1.2.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e8e575e528393418b57488afb653a8baa09afa06f97b88e86fcc7d984fb5bba6
4
- data.tar.gz: a6a00f4e7c432fa0cfd77f6cc22b3cbbdc936043d12ac68e337d4500e8bb42d8
3
+ metadata.gz: 83a5a4bbb01bcbd6b9837e72533d2ce4ef7b7515435da663a5f098e2ac64dde0
4
+ data.tar.gz: 227036f85eb431f3a7feab16e24c994d7702e168df5e773b53c6a89ed79368a0
5
5
  SHA512:
6
- metadata.gz: 892f21ee6b9d43f943cbb562ae72d62e936928acc562752f8d3a7b42c3713a7281408fa401f49eb6c6394d34659131c0288c4c7832c3e5608bb58f67ee040ed3
7
- data.tar.gz: 047ce3c0207470966af51a017d71ee23bcfa03fbe0a4371defdc4a1405e697549e519349af71ef5c3a6bcdfc8cc41d85201106d19a672d68e2fcbe8ae5d73fa9
6
+ metadata.gz: fecb0e599904792539a4656d7bea043a14a0eb7f90c5fc6f59b450f32b1cbeb326fab1837a445df107536fb750ad83b4eda0f446b9231aaaebe19bcc3f7861c8
7
+ data.tar.gz: '039460f3aacc5981e3f820210dee37888990bb7930baa142eef457d10fe04d0a977ef955f5dd2b9169ffa78867879c0fe0a9ffe590db198a556bf1f3397251d5'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- skull_island (1.2.7)
4
+ skull_island (1.2.8)
5
5
  deepsort (~> 0.4)
6
6
  erubi (~> 1.8)
7
7
  json (~> 2.1)
data/README.md CHANGED
@@ -85,6 +85,10 @@ skull_island export --verbose /path/to/export.yml
85
85
 
86
86
  Exporting, by default, exports the entire configuration of a Kong gateway, but will strip out special meta-data tags added by Skull Island to track projects. If, instead, you'd like to export **only** the configuration for a specific project, you can add `--project foo` (where `foo` is the name of your project) to export only those resources associated with it and maintain the special key in the exported YAML.
87
87
 
88
+ #### Exporting Credentials
89
+
90
+ For most credential types, exporting works as expected (you'll see the plaintext value in the exported YAML). With `BasicauthCredential`s, however, this is not the case. This isn't a limitation of `skull_island`; rather, it is the [expected behavior](https://github.com/Kong/kong/issues/4237) of the Admin API developers. This tool, when exporting these credentials, can only provide the salted-SHA1 hash of the password, and it does so by wrapping it in a special `hash{}` notation. This allows `skull_island` to distinguish between changing the value to the literal string and comparing the _hashed_ values. The import process is also smart enough to compare plaintext to hashed values returned from the API for existing values, so it won't recreate credentials every run.
91
+
88
92
  ### Importing
89
93
 
90
94
  Skull Island also supports importing configurations (both partial and full) from a YAML + ERB document:
@@ -182,6 +186,8 @@ certificates: []
182
186
  consumers:
183
187
  - username: foo
184
188
  custom_id: foo
189
+ acls:
190
+ - group: searchusers
185
191
  credentials:
186
192
  key-auth:
187
193
  - key: q90r8908w09rqw9jfj09jq0f8y389
@@ -233,6 +239,13 @@ plugins:
233
239
  - x-api-key
234
240
  run_on_preflight: true
235
241
  service: "<%= lookup :service, 'search_api' %>"
242
+ - name: acl
243
+ enabled: true
244
+ config:
245
+ hide_groups_header: false
246
+ whitelist:
247
+ - searchusers
248
+ service: "<%= lookup :service, 'search_api' %>"
236
249
  ```
237
250
 
238
251
  All top-level keys (other than `version` and `project`) require an Array as a parameter, either by providing a list of entries or an empty Array (`[]`). The above shows how to use the `lookup()` function to refer to another resource. This "looks up" the resource type (`service` in this case) by `name` (`search_api` in this case) and resolves its `id`. This function can also be used to lookup a `route` or `upstream` by its `name`, or a `consumer` by its `username`. Note that Kong itself doesn't _require_ `route` resources to have unique names, so you'll need to enforce that practice yourself for `lookup` to be useful for Routes.
@@ -376,7 +389,7 @@ resource.created_at
376
389
  # => #<DateTime: 2018-07-17T12:51:28+00:00 ((2458317j,46288s,0n),+0s,2299161j)>
377
390
  ```
378
391
 
379
- #### Consumers (and their Credentials)
392
+ #### Consumers (along with their Access Control Lists and Credentials)
380
393
 
381
394
  Note that for Consumer credentials, only [`key-auth`](https://docs.konghq.com/hub/kong-inc/key-auth/), [`jwt`](https://docs.konghq.com/hub/kong-inc/jwt/), and [`basic-auth`](https://docs.konghq.com/hub/kong-inc/basic-auth/) are currently supported.
382
395
 
@@ -397,6 +410,8 @@ resource.created_at
397
410
  # => #<DateTime: 2018-07-17T12:51:28+00:00 ((2458317j,46288s,0n),+0s,2299161j)>
398
411
  resource.plugins
399
412
  # => #<SkullIsland::ResourceCollection:0x00007f9f1e564f3e...
413
+ resource.acls
414
+ # => #<SkullIsland::ResourceCollection:0x00007f9f1e765b3c...
400
415
  resource.credentials
401
416
  # => {}
402
417
  resource.add_credential!(key: '932948e89e09e2989d8092') # adds a KeyauthCredential
@@ -411,6 +426,8 @@ resource.credentials['basic-auth']
411
426
  # => #<SkullIsland::ResourceCollection:0x00007f9f1e564f3f...
412
427
  resource.credentials['basic-auth'].first.username
413
428
  # => "test"
429
+ resource.add_acl!(group: 'somegroup')
430
+ # => true
414
431
  ```
415
432
 
416
433
  #### Plugins
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SkullIsland
4
+ # Resource classes go here...
5
+ module Resources
6
+ # The ACL resource class
7
+ #
8
+ # @see https://docs.konghq.com/hub/kong-inc/acl/ ACL API definition
9
+ class AccessControlList < Resource
10
+ property :group, validate: true
11
+ property(
12
+ :consumer,
13
+ required: true, validate: true, preprocess: true, postprocess: true
14
+ )
15
+ property :created_at, read_only: true, postprocess: true
16
+
17
+ def self.batch_import(data, verbose: false, test: false)
18
+ raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
19
+
20
+ known_ids = []
21
+
22
+ data.each_with_index do |resource_data, index|
23
+ resource = new
24
+ resource.delayed_set(:group, resource_data, 'group')
25
+ resource.delayed_set(:consumer, resource_data, 'consumer')
26
+ resource.import_update_or_skip(index: index, verbose: verbose, test: test)
27
+ known_ids << resource.id
28
+ end
29
+
30
+ known_ids
31
+ end
32
+
33
+ def self.relative_uri
34
+ 'acls'
35
+ end
36
+
37
+ def relative_uri
38
+ consumer ? "#{consumer.relative_uri}/acls/#{id}" : nil
39
+ end
40
+
41
+ def save_uri
42
+ consumer ? "#{consumer.relative_uri}/acls" : nil
43
+ end
44
+
45
+ def export(options = {})
46
+ hash = { 'group' => group }
47
+ hash['consumer'] = "<%= lookup :consumer, '#{consumer.username}' %>" if consumer
48
+ [*options[:exclude]].each do |exclude|
49
+ hash.delete(exclude.to_s)
50
+ end
51
+ [*options[:include]].each do |inc|
52
+ hash[inc.to_s] = send(inc.to_sym)
53
+ end
54
+ hash.reject { |_, value| value.nil? }
55
+ end
56
+
57
+ # Keys can't be updated, only created or deleted
58
+ def modified_existing?
59
+ false
60
+ end
61
+
62
+ def project
63
+ consumer ? consumer.project : nil
64
+ end
65
+
66
+ private
67
+
68
+ def postprocess_consumer(value)
69
+ if value.is_a?(Hash)
70
+ Consumer.new(
71
+ entity: value,
72
+ lazy: true,
73
+ tainted: false
74
+ )
75
+ else
76
+ value
77
+ end
78
+ end
79
+
80
+ def preprocess_consumer(input)
81
+ if input.is_a?(Hash)
82
+ input
83
+ else
84
+ { 'id' => input.id }
85
+ end
86
+ end
87
+
88
+ # Used to validate {#consumer} on set
89
+ def validate_consumer(value)
90
+ # allow either a Consumer object or a Hash
91
+ value.is_a?(Consumer) || value.is_a?(Hash)
92
+ end
93
+
94
+ # Used to validate {#group} on set
95
+ def validate_group(value)
96
+ # allow a String
97
+ value.is_a?(String)
98
+ end
99
+ end
100
+ end
101
+ end
@@ -17,6 +17,7 @@ module SkullIsland
17
17
  # rubocop:disable Metrics/CyclomaticComplexity
18
18
  # rubocop:disable Metrics/PerceivedComplexity
19
19
  # rubocop:disable Metrics/AbcSize
20
+ # rubocop:disable Metrics/MethodLength
20
21
  def self.batch_import(data, verbose: false, test: false, project: nil, time: nil)
21
22
  raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
22
23
 
@@ -57,6 +58,14 @@ module SkullIsland
57
58
  test: test
58
59
  )
59
60
 
61
+ known_acls = AccessControlList.batch_import(
62
+ (
63
+ resource_data.dig('acls') || []
64
+ ).map { |t| t.merge('consumer' => { 'id' => resource.id }) },
65
+ verbose: verbose,
66
+ test: test
67
+ )
68
+
60
69
  next unless project
61
70
 
62
71
  basic_creds = BasicauthCredential.all.select { |c| c.consumer == resource }
@@ -76,11 +85,38 @@ module SkullIsland
76
85
  puts "[WARN] ! Removing #{res.class.name} (#{res.id})"
77
86
  res.destroy
78
87
  end
88
+
89
+ acls = AccessControlList.all.select { |acl| acl.consumer == resource }
90
+ acls.reject { |res| known_acls.include?(res.id) }.map do |res|
91
+ puts "[WARN] ! Removing #{res.class.name} (#{res.id})"
92
+ res.destroy
93
+ end
79
94
  end
80
95
  # rubocop:enable Metrics/BlockLength
81
96
 
82
97
  cleanup_except(project, known_ids) if project
83
98
  end
99
+ # rubocop:enable Metrics/MethodLength
100
+
101
+ def acls
102
+ AccessControlList.where(:consumer, self, api_client: api_client)
103
+ end
104
+
105
+ def add_acl!(details)
106
+ r = if details.is_a?(AccessControlList)
107
+ details
108
+ elsif details.is_a?(String)
109
+ resource = AccessControlList.new(api_client: api_client)
110
+ resource.group = details
111
+ resource
112
+ else
113
+ resource = AccessControlList.new(api_client: api_client)
114
+ resource.group = details[:group]
115
+ resource
116
+ end
117
+ r.consumer = self
118
+ r.save
119
+ end
84
120
 
85
121
  def add_credential!(details)
86
122
  r = if [BasicauthCredential, JWTCredential, KeyauthCredential].include? details.class
@@ -130,6 +166,7 @@ module SkullIsland
130
166
  hash = { 'username' => username, 'custom_id' => custom_id }
131
167
  creds = credentials_for_export
132
168
  hash['credentials'] = creds unless creds.empty?
169
+ hash['acls'] = acls.map { |acl| acl.export(exclude: 'consumer') } unless acls.empty?
133
170
  hash['tags'] = tags unless tags.empty?
134
171
  [*options[:exclude]].each do |exclude|
135
172
  hash.delete(exclude.to_s)
@@ -59,11 +59,12 @@ module SkullIsland
59
59
  super.reject { |k| %i[run_on].include? k }
60
60
  end
61
61
 
62
+ # rubocop:disable Metrics/AbcSize
62
63
  def export(options = {})
63
64
  hash = {
64
65
  'name' => name,
65
66
  'enabled' => enabled?,
66
- 'config' => config.deep_sort
67
+ 'config' => config.deep_sort.compact
67
68
  }
68
69
  hash['consumer'] = "<%= lookup :consumer, '#{consumer.username}' %>" if consumer
69
70
  hash['route'] = "<%= lookup :route, '#{route.name}' %>" if route
@@ -78,6 +79,7 @@ module SkullIsland
78
79
  hash.reject { |_, value| value.nil? }
79
80
  end
80
81
 
82
+ # rubocop:disable Metrics/CyclomaticComplexity
81
83
  # rubocop:disable Metrics/PerceivedComplexity
82
84
  def modified_existing?
83
85
  return false unless new?
@@ -86,14 +88,15 @@ module SkullIsland
86
88
  same_name = self.class.where(:name, name)
87
89
  return false if same_name.size.zero?
88
90
 
89
- same_name_and_consumer = same_name.where(:consumer, consumer)
90
- same_name_and_route = same_name.where(:route, route)
91
- same_name_and_service = same_name.where(:service, service)
92
- existing = if same_name_and_consumer.size == 1
91
+ same_name_and_consumer = consumer ? same_name.where(:consumer, consumer) : nil
92
+ same_name_and_route = route ? same_name.where(:route, route) : nil
93
+ same_name_and_service = service ? same_name.where(:service, service) : nil
94
+
95
+ existing = if same_name_and_consumer && same_name_and_consumer.size == 1
93
96
  same_name_and_consumer.first
94
- elsif same_name_and_route.size == 1
97
+ elsif same_name_and_route && same_name_and_route.size == 1
95
98
  same_name_and_route.first
96
- elsif same_name_and_service.size == 1
99
+ elsif same_name_and_service && same_name_and_service.size == 1
97
100
  same_name_and_service.first
98
101
  end
99
102
  if existing
@@ -104,6 +107,8 @@ module SkullIsland
104
107
  end
105
108
  end
106
109
  # rubocop:enable Metrics/PerceivedComplexity
110
+ # rubocop:enable Metrics/CyclomaticComplexity
111
+ # rubocop:enable Metrics/AbcSize
107
112
 
108
113
  private
109
114
 
@@ -112,7 +117,7 @@ module SkullIsland
112
117
  end
113
118
 
114
119
  def postprocess_config(value)
115
- value.deep_sort
120
+ value.deep_sort.compact
116
121
  end
117
122
 
118
123
  def postprocess_consumer(value)
@@ -4,6 +4,6 @@ module SkullIsland
4
4
  VERSION = [
5
5
  1, # Major
6
6
  2, # Minor
7
- 7 # Patch
7
+ 8 # Patch
8
8
  ].join('.')
9
9
  end
data/lib/skull_island.rb CHANGED
@@ -45,6 +45,7 @@ require 'skull_island/helpers/resource_class'
45
45
  require 'skull_island/helpers/migration'
46
46
  require 'skull_island/validations/resource'
47
47
  require 'skull_island/resource'
48
+ require 'skull_island/resources/access_control_list'
48
49
  require 'skull_island/resources/certificate'
49
50
  require 'skull_island/resources/basicauth_credential'
50
51
  require 'skull_island/resources/jwt_credential'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: skull_island
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.7
4
+ version: 1.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Gnagy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-05 00:00:00.000000000 Z
11
+ date: 2019-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: deepsort
@@ -249,6 +249,7 @@ files:
249
249
  - lib/skull_island/lru_cache.rb
250
250
  - lib/skull_island/resource.rb
251
251
  - lib/skull_island/resource_collection.rb
252
+ - lib/skull_island/resources/access_control_list.rb
252
253
  - lib/skull_island/resources/basicauth_credential.rb
253
254
  - lib/skull_island/resources/certificate.rb
254
255
  - lib/skull_island/resources/consumer.rb