audiences 2.0.0 → 2.0.1
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/app/assets/builds/audiences-ujs.js +142 -132
- data/app/events/audiences/application_event.rb +7 -0
- data/app/events/audiences/persisted_resource_event.rb +10 -0
- data/app/models/audiences/external_user.rb +13 -28
- data/app/models/audiences/group_membership.rb +2 -0
- data/db/migrate/20260506150000_add_unique_index_to_group_memberships.rb +10 -0
- data/docs/CHANGELOG.md +4 -0
- data/lib/audiences/configuration.rb +18 -1
- data/lib/audiences/scim/field_mapping.rb +32 -4
- data/lib/audiences/scim/upsert_groups_observer.rb +1 -0
- data/lib/audiences/scim/upsert_users_observer.rb +16 -2
- data/lib/audiences/version.rb +1 -1
- metadata +5 -2
|
@@ -79,37 +79,22 @@ module Audiences
|
|
|
79
79
|
"title" => names["Titles"],
|
|
80
80
|
"urn:ietf:params:scim:schemas:extension:authservice:2.0:User" => {
|
|
81
81
|
"role" => names["Roles"], "department" => names["Departments"],
|
|
82
|
-
"territory" => names["Territories"], "territoryAbbr" =>
|
|
82
|
+
"territory" => names["Territories"], "territoryAbbr" => territory_abbr(names["Territories"])
|
|
83
83
|
},
|
|
84
84
|
}
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
"Denver" => "DEN",
|
|
100
|
-
"Tampa" => "TPA",
|
|
101
|
-
"Austin" => "AUS",
|
|
102
|
-
"Charlotte" => "CLT",
|
|
103
|
-
"Nashville" => "NSH",
|
|
104
|
-
"Phoenix" => "PHX",
|
|
105
|
-
"Pittsburgh" => "PIT",
|
|
106
|
-
"San Antonio" => "SAO",
|
|
107
|
-
"Fort Lauderdale" => "FLL",
|
|
108
|
-
"Las Vegas" => "LVS",
|
|
109
|
-
"Orlando" => "ORL",
|
|
110
|
-
"Cincinnati" => "CIN",
|
|
111
|
-
"Columbus" => "CLB",
|
|
112
|
-
"Jacksonville" => "JAX",
|
|
113
|
-
}.freeze
|
|
87
|
+
def missing_group_types(expected_types = Audiences.config.required_group_types)
|
|
88
|
+
return [] if expected_types.blank?
|
|
89
|
+
|
|
90
|
+
actual_types = groups.map(&:resource_type)
|
|
91
|
+
expected_types - actual_types
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def territory_abbr(territory)
|
|
97
|
+
Audiences.config.territory_abbreviations[territory]
|
|
98
|
+
end
|
|
114
99
|
end
|
|
115
100
|
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddUniqueIndexToGroupMemberships < ActiveRecord::Migration[6.1]
|
|
4
|
+
def change
|
|
5
|
+
add_index :audiences_group_memberships,
|
|
6
|
+
%i[group_id external_user_id],
|
|
7
|
+
unique: true,
|
|
8
|
+
name: "index_group_memberships_on_group_and_user"
|
|
9
|
+
end
|
|
10
|
+
end
|
data/docs/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Unreleased
|
|
2
2
|
|
|
3
|
+
# Version 2.0.1 (2026-05-08)
|
|
4
|
+
|
|
5
|
+
- Fixes a performance issue where we load all users when updating memberships [#557](https://github.com/powerhome/audiences/pull/557)
|
|
6
|
+
|
|
3
7
|
# Version 2.0 (2025-08-25)
|
|
4
8
|
|
|
5
9
|
The all new 2.0 release inverts the SCIM logic, where now Audiences no longer pulls data from SCIM, but rather it will receive and cache SCIM data, allowing for an in database calculation of audiences. This improved process allows audiences to be more independent from SCIM, while still compatible with the protocol.
|
|
@@ -16,6 +16,23 @@ module Audiences
|
|
|
16
16
|
%w[Groups]
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
# Group types that must be present in a user provisioning event
|
|
20
|
+
config_accessor :required_group_types do
|
|
21
|
+
[]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
DEFAULT_TERRITORY_ABBREVIATIONS = {
|
|
25
|
+
"Philadelphia" => "PHL", "New Jersey" => "NJ", "Maryland" => "MD", "Connecticut" => "CT",
|
|
26
|
+
"Long Island" => "LI", "Boston" => "BOS", "Atlanta" => "ATL", "Chicago" => "CHI",
|
|
27
|
+
"Detroit" => "DET", "Houston" => "HOU", "Dallas" => "DAL", "Denver" => "DEN", "Tampa" => "TPA",
|
|
28
|
+
"Austin" => "AUS", "Charlotte" => "CLT", "Nashville" => "NSH", "Phoenix" => "PHX",
|
|
29
|
+
"Pittsburgh" => "PIT", "San Antonio" => "SAO", "Fort Lauderdale" => "FLL", "Las Vegas" => "LVS",
|
|
30
|
+
"Orlando" => "ORL", "Cincinnati" => "CIN", "Columbus" => "CLB", "Jacksonville" => "JAX",
|
|
31
|
+
"Oklahoma City" => "OKC", "Raleigh" => "RLD", "Cleveland" => "CLE"
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
config_accessor(:territory_abbreviations) { DEFAULT_TERRITORY_ABBREVIATIONS }
|
|
35
|
+
|
|
19
36
|
# Defines a default scope for users, so the users that are part of an audience can
|
|
20
37
|
# be filtered (i.e.: only active, only users in a specific group, etc)
|
|
21
38
|
#
|
|
@@ -98,7 +115,7 @@ module Audiences
|
|
|
98
115
|
Audiences.logger.warn(<<~MESSAGE)
|
|
99
116
|
Audiences authenticate is currently configured using a default and is blocking authenticaiton.
|
|
100
117
|
|
|
101
|
-
To make this
|
|
118
|
+
To make this warning go away provide a configuration for `Audiences.config.authenticate`.
|
|
102
119
|
|
|
103
120
|
The value should:
|
|
104
121
|
1. Be callable like a Proc.
|
|
@@ -8,13 +8,27 @@ module Audiences
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def remove(object, path, val)
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
return unless @map.key?(path)
|
|
12
|
+
|
|
13
|
+
case @map[path]
|
|
14
|
+
in { to: to, find: find }
|
|
15
|
+
remove_from_association(object, to, find, val)
|
|
16
|
+
else
|
|
17
|
+
current = object.send to(path)
|
|
18
|
+
_set object, path, current - value(path, val)
|
|
19
|
+
end
|
|
13
20
|
end
|
|
14
21
|
|
|
15
22
|
def add(object, path, val)
|
|
16
|
-
|
|
17
|
-
|
|
23
|
+
return unless @map.key?(path)
|
|
24
|
+
|
|
25
|
+
case @map[path]
|
|
26
|
+
in { to: to, find: find }
|
|
27
|
+
add_to_association(object, to, find, val)
|
|
28
|
+
else
|
|
29
|
+
current = object.send to(path)
|
|
30
|
+
_set object, path, current + value(path, val)
|
|
31
|
+
end
|
|
18
32
|
end
|
|
19
33
|
|
|
20
34
|
def replace(object, path, val)
|
|
@@ -44,6 +58,20 @@ module Audiences
|
|
|
44
58
|
else val
|
|
45
59
|
end
|
|
46
60
|
end
|
|
61
|
+
|
|
62
|
+
def add_to_association(object, to, find, val)
|
|
63
|
+
# Use << operator to avoid loading all records
|
|
64
|
+
collection = object.send(to)
|
|
65
|
+
new_items = [val].flatten.pluck("value").filter_map(&find)
|
|
66
|
+
new_items.each { |item| collection << item unless collection.include?(item) }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def remove_from_association(object, to, find, val)
|
|
70
|
+
# Use delete operator to avoid loading all records
|
|
71
|
+
collection = object.send(to)
|
|
72
|
+
items_to_remove = [val].flatten.pluck("value").filter_map(&find)
|
|
73
|
+
collection.delete(*items_to_remove)
|
|
74
|
+
end
|
|
47
75
|
end
|
|
48
76
|
end
|
|
49
77
|
end
|
|
@@ -12,6 +12,7 @@ module Audiences
|
|
|
12
12
|
Audiences.logger.info "#{upsert_action} group #{new_display_name} (#{new_external_id})"
|
|
13
13
|
|
|
14
14
|
group.update! external_id: new_external_id, display_name: new_display_name, active: new_active
|
|
15
|
+
Audiences::PersistedResourceEvent.create(resource_type: "Groups", params: event_payload.params)
|
|
15
16
|
rescue => e
|
|
16
17
|
Audiences.logger.error e
|
|
17
18
|
raise
|
|
@@ -7,9 +7,11 @@ module Audiences
|
|
|
7
7
|
subscribe_to "two_percent.scim.replace.Users"
|
|
8
8
|
|
|
9
9
|
def process
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
log_upsert_action
|
|
12
11
|
external_user.update! updated_attributes
|
|
12
|
+
return unless valid_group_types?
|
|
13
|
+
|
|
14
|
+
Audiences::PersistedResourceEvent.create(resource_type: "Users", params: event_payload.params)
|
|
13
15
|
rescue => e
|
|
14
16
|
Audiences.logger.error e
|
|
15
17
|
raise
|
|
@@ -17,6 +19,10 @@ module Audiences
|
|
|
17
19
|
|
|
18
20
|
private
|
|
19
21
|
|
|
22
|
+
def log_upsert_action
|
|
23
|
+
Audiences.logger.info "#{upsert_action} user #{event_payload.params['displayName']} (#{scim_id})"
|
|
24
|
+
end
|
|
25
|
+
|
|
20
26
|
def scim_id = event_payload.params["id"]
|
|
21
27
|
|
|
22
28
|
def external_user
|
|
@@ -43,6 +49,14 @@ module Audiences
|
|
|
43
49
|
Audiences::Group.find_by(scim_id: group["value"])
|
|
44
50
|
end
|
|
45
51
|
end
|
|
52
|
+
|
|
53
|
+
def valid_group_types?
|
|
54
|
+
missing = external_user.missing_group_types
|
|
55
|
+
return true if missing.empty?
|
|
56
|
+
|
|
57
|
+
Audiences.logger.warn "Provisioning event for user #{scim_id} with missing group types: #{missing.join(', ')}"
|
|
58
|
+
false
|
|
59
|
+
end
|
|
46
60
|
end
|
|
47
61
|
end
|
|
48
62
|
end
|
data/lib/audiences/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: audiences
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Carlos Palhares
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-05-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: aether_observatory
|
|
@@ -51,6 +51,8 @@ files:
|
|
|
51
51
|
- app/controllers/audiences/application_controller.rb
|
|
52
52
|
- app/controllers/audiences/contexts_controller.rb
|
|
53
53
|
- app/controllers/audiences/scim_proxy_controller.rb
|
|
54
|
+
- app/events/audiences/application_event.rb
|
|
55
|
+
- app/events/audiences/persisted_resource_event.rb
|
|
54
56
|
- app/models/audiences/application_record.rb
|
|
55
57
|
- app/models/audiences/context.rb
|
|
56
58
|
- app/models/audiences/context/locating.rb
|
|
@@ -85,6 +87,7 @@ files:
|
|
|
85
87
|
- db/migrate/20250624171247_create_audiences_context_extra_users.rb
|
|
86
88
|
- db/migrate/20250624171706_rename_audiences_context_extra_users_to_extra_users_json.rb
|
|
87
89
|
- db/migrate/20250701173946_move_extra_users_to_context_extra_users.rb
|
|
90
|
+
- db/migrate/20260506150000_add_unique_index_to_group_memberships.rb
|
|
88
91
|
- docs/CHANGELOG.md
|
|
89
92
|
- docs/README.md
|
|
90
93
|
- lib/audiences.rb
|