skull_island 1.2.7 → 1.2.8

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 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