entitlements-github-plugin 0.2.0 → 0.4.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.
- checksums.yaml +4 -4
- data/lib/entitlements/backend/github_org/controller.rb +5 -5
- data/lib/entitlements/backend/github_org/service.rb +2 -2
- data/lib/entitlements/backend/github_team/controller.rb +3 -3
- data/lib/entitlements/backend/github_team/models/team.rb +1 -1
- data/lib/entitlements/backend/github_team/provider.rb +2 -2
- data/lib/entitlements/backend/github_team/service.rb +60 -11
- data/lib/entitlements/service/github.rb +3 -3
- data/lib/version.rb +7 -0
- metadata +7 -7
- data/VERSION +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c768cc8f03a6eae9aaf1350173a59274b53d51cb38b7df20e798ea2e49b049e9
|
4
|
+
data.tar.gz: 1d66f2116e098a02cd81ca0f9a50c98995cda306ff3aceba0e2f37fc705f3b93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47b00156083eabe37d014eaccbca358c291e7c0ebd4d48f0da8225f347526fdd6cef79969ec152d14db258a8412dc4ab73b1aa35742cf904e135206c85cca51b
|
7
|
+
data.tar.gz: c9f604456245070f5b6ff2dd89b63a16bd36f052f8b489aad4c72b0d85e06782789b5697099053e0f2176a90884c303b5c75e28c9d5c4fb2944267619fde5e25
|
@@ -83,7 +83,7 @@ module Entitlements
|
|
83
83
|
validate_no_dupes! # calls read() for each group
|
84
84
|
|
85
85
|
if changes.any?
|
86
|
-
print_differences(key: group_name, added: [], removed: [], changed: changes, ignored_users:
|
86
|
+
print_differences(key: group_name, added: [], removed: [], changed: changes, ignored_users:)
|
87
87
|
@actions.concat(changes)
|
88
88
|
else
|
89
89
|
logger.debug "UNCHANGED: No GitHub organization changes for #{group_name}"
|
@@ -398,11 +398,11 @@ module Entitlements
|
|
398
398
|
if removed.key?(member.downcase)
|
399
399
|
# Already removed from a previous role. Therefore this is a move to a different role.
|
400
400
|
removed.delete(member.downcase)
|
401
|
-
moved[member.downcase] = { member
|
401
|
+
moved[member.downcase] = { member:, role: }
|
402
402
|
else
|
403
403
|
# Not removed from a previous role. Suspect this is an addition to the org (if we later spot a removal
|
404
404
|
# from a role, then the code below will update that to be a move instead).
|
405
|
-
added[member.downcase] = { member
|
405
|
+
added[member.downcase] = { member:, role: }
|
406
406
|
end
|
407
407
|
end
|
408
408
|
|
@@ -414,12 +414,12 @@ module Entitlements
|
|
414
414
|
else
|
415
415
|
# Not added to a previous role. Suspect this is a removal from the org (if we later spot an addition
|
416
416
|
# to another role, then the code above will update that to be a move instead).
|
417
|
-
removed[member.downcase] = { member
|
417
|
+
removed[member.downcase] = { member:, role: }
|
418
418
|
end
|
419
419
|
end
|
420
420
|
end
|
421
421
|
|
422
|
-
{ added
|
422
|
+
{ added:, removed:, moved: }
|
423
423
|
end
|
424
424
|
|
425
425
|
# Admins or members who are both `invited` and `pending` do not need to be re-invited. We're waiting for them
|
@@ -44,7 +44,7 @@ module Entitlements
|
|
44
44
|
Contract String, String => C::Bool
|
45
45
|
def add_user_to_organization(user, role)
|
46
46
|
Entitlements.logger.debug "#{identifier} add_user_to_organization(user=#{user}, org=#{org}, role=#{role})"
|
47
|
-
new_membership = octokit.update_organization_membership(org, user
|
47
|
+
new_membership = octokit.update_organization_membership(org, user:, role:)
|
48
48
|
|
49
49
|
# Happy path
|
50
50
|
if new_membership[:role] == role
|
@@ -70,7 +70,7 @@ module Entitlements
|
|
70
70
|
Contract String => C::Bool
|
71
71
|
def remove_user_from_organization(user)
|
72
72
|
Entitlements.logger.debug "#{identifier} remove_user_from_organization(user=#{user}, org=#{org})"
|
73
|
-
result = octokit.remove_organization_membership(org, user:
|
73
|
+
result = octokit.remove_organization_membership(org, user:)
|
74
74
|
|
75
75
|
# If we removed the user, remove them from the cache of members, so that any GitHub team
|
76
76
|
# operations in this organization will ignore this user.
|
@@ -61,12 +61,12 @@ module Entitlements
|
|
61
61
|
end
|
62
62
|
|
63
63
|
if diff[:metadata] && diff[:metadata][:create_team]
|
64
|
-
added << Entitlements::Models::Action.new(team_slug, provider.read(group), group, group_name, ignored_users:
|
64
|
+
added << Entitlements::Models::Action.new(team_slug, provider.read(group), group, group_name, ignored_users:)
|
65
65
|
else
|
66
|
-
changed << Entitlements::Models::Action.new(team_slug, provider.read(group), group, group_name, ignored_users:
|
66
|
+
changed << Entitlements::Models::Action.new(team_slug, provider.read(group), group, group_name, ignored_users:)
|
67
67
|
end
|
68
68
|
end
|
69
|
-
print_differences(key: group_name, added
|
69
|
+
print_differences(key: group_name, added:, removed: [], changed:)
|
70
70
|
|
71
71
|
@actions = added + changed
|
72
72
|
end
|
@@ -27,7 +27,7 @@ module Entitlements
|
|
27
27
|
@team_id = team_id
|
28
28
|
@team_name = team_name.downcase
|
29
29
|
@team_dn = ["cn=#{team_name.downcase}", ou].join(",")
|
30
|
-
super(dn: @team_dn, members: Set.new(members.map { |m| m.downcase }), metadata:
|
30
|
+
super(dn: @team_dn, members: Set.new(members.map { |m| m.downcase }), metadata:)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
@@ -127,7 +127,7 @@ module Entitlements
|
|
127
127
|
# Create the new team and invalidate the cache
|
128
128
|
if github_team.nil?
|
129
129
|
team_name = entitlement_group.cn.downcase
|
130
|
-
github.create_team(entitlement_group:
|
130
|
+
github.create_team(entitlement_group:)
|
131
131
|
github.invalidate_predictive_cache(entitlement_group)
|
132
132
|
@github_team_cache.delete(team_name)
|
133
133
|
github_team = github.read_team(entitlement_group)
|
@@ -168,7 +168,7 @@ module Entitlements
|
|
168
168
|
team_name: entitlement_group.cn.downcase,
|
169
169
|
members: Set.new,
|
170
170
|
ou: github.ou,
|
171
|
-
metadata:
|
171
|
+
metadata:
|
172
172
|
)
|
173
173
|
end
|
174
174
|
|
@@ -4,6 +4,7 @@ require_relative "models/team"
|
|
4
4
|
require_relative "../../service/github"
|
5
5
|
|
6
6
|
require "base64"
|
7
|
+
require "set"
|
7
8
|
|
8
9
|
module Entitlements
|
9
10
|
class Backend
|
@@ -77,7 +78,7 @@ module Entitlements
|
|
77
78
|
team_id: -1,
|
78
79
|
team_name: team_identifier,
|
79
80
|
members: cached_members,
|
80
|
-
ou
|
81
|
+
ou:,
|
81
82
|
metadata: team_metadata
|
82
83
|
)
|
83
84
|
|
@@ -107,11 +108,11 @@ module Entitlements
|
|
107
108
|
team_id: teamdata[:team_id],
|
108
109
|
team_name: team_identifier,
|
109
110
|
members: Set.new(teamdata[:members]),
|
110
|
-
ou
|
111
|
+
ou:,
|
111
112
|
metadata: team_metadata
|
112
113
|
)
|
113
114
|
rescue TeamNotFound
|
114
|
-
Entitlements.logger.warn "Team #{team_identifier} does not exist in this GitHub.com organization"
|
115
|
+
Entitlements.logger.warn "Team #{team_identifier} does not exist in this GitHub.com organization. If applied, the team will be created."
|
115
116
|
return nil
|
116
117
|
end
|
117
118
|
|
@@ -196,14 +197,56 @@ module Entitlements
|
|
196
197
|
end
|
197
198
|
end
|
198
199
|
|
199
|
-
|
200
|
-
|
200
|
+
desired_team_members = Set.new(desired_state.member_strings.map { |u| u.downcase })
|
201
|
+
current_team_members = Set.new(current_state.member_strings.map { |u| u.downcase })
|
202
|
+
|
203
|
+
added_members = desired_team_members - current_team_members
|
204
|
+
removed_members = current_team_members - desired_team_members
|
201
205
|
|
202
206
|
added_members.select! { |username| add_user_to_team(user: username, team: current_state) }
|
203
207
|
removed_members.select! { |username| remove_user_from_team(user: username, team: current_state) }
|
204
208
|
|
209
|
+
added_maintainers = Set.new
|
210
|
+
removed_maintainers = Set.new
|
211
|
+
unless desired_metadata["team_maintainers"] == current_metadata["team_maintainers"]
|
212
|
+
if desired_metadata["team_maintainers"].nil?
|
213
|
+
# We will not delete ALL maintainers from a team and leave it without maintainers.
|
214
|
+
Entitlements.logger.debug "sync_team(#{current_state.team_name}=#{current_state.team_id}): IGNORING GitHub Team Maintainer DELETE"
|
215
|
+
else
|
216
|
+
desired_maintainers_str = desired_metadata["team_maintainers"] # not nil, we tested that above
|
217
|
+
desired_maintainers = Set.new(desired_maintainers_str.split(",").map { |u| u.strip.downcase })
|
218
|
+
unless desired_maintainers.subset?(desired_team_members)
|
219
|
+
maintainer_not_member = desired_maintainers - desired_team_members
|
220
|
+
Entitlements.logger.warn "sync_team(#{current_state.team_name}=#{current_state.team_id}): Maintainers must be a subset of team members. Desired maintainers: #{maintainer_not_member.to_a} are not members. Ignoring."
|
221
|
+
desired_maintainers = desired_maintainers.intersection(desired_team_members)
|
222
|
+
end
|
223
|
+
|
224
|
+
current_maintainers_str = current_metadata["team_maintainers"]
|
225
|
+
current_maintainers = Set.new(
|
226
|
+
current_maintainers_str.nil? ? [] : current_maintainers_str.split(",").map { |u| u.strip.downcase }
|
227
|
+
)
|
228
|
+
# We ignore any current maintainer who is not a member of the team according to the team spec
|
229
|
+
# This avoids messing with teams who have been manually modified to add a maintainer
|
230
|
+
current_maintainers = current_maintainers.intersection(desired_team_members)
|
231
|
+
added_maintainers = desired_maintainers - current_maintainers
|
232
|
+
removed_maintainers = current_maintainers - desired_maintainers
|
233
|
+
if added_maintainers.empty? && removed_maintainers.empty?
|
234
|
+
Entitlements.logger.debug "sync_team(#{current_state.team_name}=#{current_state.team_id}): Textual change but no semantic change in maintainers. It is remains: #{current_maintainers.to_a}."
|
235
|
+
else
|
236
|
+
Entitlements.logger.debug "sync_team(#{current_state.team_name}=#{current_state.team_id}): Maintainer members change found - From #{current_maintainers.to_a} to #{desired_maintainers.to_a}"
|
237
|
+
added_maintainers.select! { |username| add_user_to_team(user: username, team: current_state, role: "maintainer") }
|
238
|
+
|
239
|
+
## We only touch previous maintainers who are actually still going to be members of the team
|
240
|
+
removed_maintainers = removed_maintainers.intersection(desired_team_members)
|
241
|
+
## Downgrade membership to default (role: "member")
|
242
|
+
removed_maintainers.select! { |username| add_user_to_team(user: username, team: current_state, role: "member") }
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
|
205
248
|
Entitlements.logger.debug "sync_team(#{current_state.team_name}=#{current_state.team_id}): Added #{added_members.count}, removed #{removed_members.count}"
|
206
|
-
added_members.any? || removed_members.any? || changed_parent_team
|
249
|
+
added_members.any? || removed_members.any? || added_maintainers.any? || removed_maintainers.any? || changed_parent_team
|
207
250
|
end
|
208
251
|
|
209
252
|
# Create a team
|
@@ -337,7 +380,7 @@ module Entitlements
|
|
337
380
|
break
|
338
381
|
end
|
339
382
|
|
340
|
-
{ members: result, team_id
|
383
|
+
{ members: result, team_id:, parent_team_name: }
|
341
384
|
end
|
342
385
|
|
343
386
|
# Ensure that the given team ID actually matches up to the team slug on GitHub. This is in place
|
@@ -366,17 +409,23 @@ module Entitlements
|
|
366
409
|
#
|
367
410
|
# user - String with the GitHub username
|
368
411
|
# team - Entitlements::Backend::GitHubTeam::Models::Team object for the team.
|
412
|
+
# role - optional (default: "member") String with the role to assign to the user: either "member" or "maintainer"
|
369
413
|
#
|
370
|
-
# Returns true if the user was added to the team
|
414
|
+
# Returns true if the user was added to the team or role changed; false if user was already on team with same role
|
371
415
|
Contract C::KeywordArgs[
|
372
416
|
user: String,
|
373
417
|
team: Entitlements::Backend::GitHubTeam::Models::Team,
|
418
|
+
role: C::Optional[String]
|
374
419
|
] => C::Bool
|
375
|
-
def add_user_to_team(user:, team:)
|
420
|
+
def add_user_to_team(user:, team:, role: "member")
|
376
421
|
return false unless org_members.include?(user.downcase)
|
377
|
-
|
422
|
+
unless role == "member" || role == "maintainer"
|
423
|
+
# :nocov:
|
424
|
+
raise "add_user_to_team role mismatch: team_id=#{team.team_id} user=#{user} expected role=maintainer/member got=#{role}"
|
425
|
+
end
|
426
|
+
Entitlements.logger.debug "#{identifier} add_user_to_team(user=#{user}, org=#{org}, team_id=#{team.team_id}, role=#{role})"
|
378
427
|
validate_team_id_and_slug!(team.team_id, team.team_name)
|
379
|
-
result = octokit.add_team_membership(team.team_id, user)
|
428
|
+
result = octokit.add_team_membership(team.team_id, user, role:)
|
380
429
|
result[:state] == "active" || result[:state] == "pending"
|
381
430
|
end
|
382
431
|
|
@@ -82,7 +82,7 @@ module Entitlements
|
|
82
82
|
member_count = result.count { |_, role| role == "member" }
|
83
83
|
Entitlements.logger.debug "Currently #{org} has #{admin_count} admin(s) and #{member_count} member(s)"
|
84
84
|
|
85
|
-
{ cache
|
85
|
+
{ cache:, value: result }
|
86
86
|
end
|
87
87
|
|
88
88
|
Entitlements.cache[:github_org_members][org_signature][:value]
|
@@ -354,9 +354,9 @@ module Entitlements
|
|
354
354
|
data = JSON.parse(response.body)
|
355
355
|
if data.key?("errors")
|
356
356
|
Entitlements.logger.error "Errors reported: #{data['errors'].inspect}"
|
357
|
-
return { code: 500, data:
|
357
|
+
return { code: 500, data: }
|
358
358
|
end
|
359
|
-
{ code: response.code.to_i, data:
|
359
|
+
{ code: response.code.to_i, data: }
|
360
360
|
rescue JSON::ParserError => e
|
361
361
|
Entitlements.logger.error "#{e.class} #{e.message}: #{response.body.inspect}"
|
362
362
|
{ code: 500, data: { "body" => response.body } }
|
data/lib/version.rb
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: entitlements-github-plugin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub, Inc. Security Ops
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: contracts
|
@@ -234,13 +234,12 @@ dependencies:
|
|
234
234
|
- - '='
|
235
235
|
- !ruby/object:Gem::Version
|
236
236
|
version: 3.4.2
|
237
|
-
description:
|
237
|
+
description: Entitlements plugin to manage GitHub Orgs and Team memberships and access
|
238
238
|
email: security@github.com
|
239
239
|
executables: []
|
240
240
|
extensions: []
|
241
241
|
extra_rdoc_files: []
|
242
242
|
files:
|
243
|
-
- VERSION
|
244
243
|
- lib/entitlements/backend/github_org.rb
|
245
244
|
- lib/entitlements/backend/github_org/controller.rb
|
246
245
|
- lib/entitlements/backend/github_org/provider.rb
|
@@ -251,11 +250,12 @@ files:
|
|
251
250
|
- lib/entitlements/backend/github_team/provider.rb
|
252
251
|
- lib/entitlements/backend/github_team/service.rb
|
253
252
|
- lib/entitlements/service/github.rb
|
253
|
+
- lib/version.rb
|
254
254
|
homepage: https://github.com/github/entitlements-github-plugin
|
255
255
|
licenses:
|
256
256
|
- MIT
|
257
257
|
metadata: {}
|
258
|
-
post_install_message:
|
258
|
+
post_install_message:
|
259
259
|
rdoc_options: []
|
260
260
|
require_paths:
|
261
261
|
- lib
|
@@ -271,7 +271,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
271
271
|
version: '0'
|
272
272
|
requirements: []
|
273
273
|
rubygems_version: 3.3.7
|
274
|
-
signing_key:
|
274
|
+
signing_key:
|
275
275
|
specification_version: 4
|
276
276
|
summary: GitHub dotcom provider for entitlements-app
|
277
277
|
test_files: []
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.2.0
|