audiences 1.6.1 → 2.0.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/app/assets/builds/audiences-ujs.js +204 -1318
- data/app/controllers/audiences/contexts_controller.rb +4 -15
- data/app/controllers/audiences/scim_proxy_controller.rb +9 -11
- data/app/models/audiences/context.rb +41 -4
- data/app/models/audiences/context_extra_user.rb +8 -0
- data/app/models/audiences/criterion.rb +42 -8
- data/app/models/audiences/criterion_group.rb +8 -0
- data/app/models/audiences/external_user.rb +92 -14
- data/app/models/audiences/group.rb +27 -0
- data/app/models/audiences/group_membership.rb +14 -0
- data/db/migrate/20250520181229_create_audiences_groups.rb +17 -0
- data/db/migrate/20250521173148_add_scim_id_to_audiences_external_users.rb +8 -0
- data/db/migrate/20250521182852_create_audiences_group_memberships.rb +12 -0
- data/db/migrate/20250527203948_populate_external_users_scim_id.rb +15 -0
- data/db/migrate/20250528130640_add_display_name_to_audiences_external_users.rb +8 -0
- data/db/migrate/20250618184332_add_active_flag_to_external_users_and_groups.rb +8 -0
- data/db/migrate/20250620005528_create_audiences_criterion_groups.rb +12 -0
- data/db/migrate/20250620005800_rename_audiences_criterion_groups_to_groups_json.rb +7 -0
- data/db/migrate/20250623131202_move_group_criterion_to_criterion_groups.rb +27 -0
- data/db/migrate/20250624171247_create_audiences_context_extra_users.rb +12 -0
- data/db/migrate/20250624171706_rename_audiences_context_extra_users_to_extra_users_json.rb +7 -0
- data/db/migrate/20250701173946_move_extra_users_to_context_extra_users.rb +21 -0
- data/docs/CHANGELOG.md +17 -0
- data/docs/README.md +0 -11
- data/lib/audiences/configuration.rb +71 -45
- data/lib/audiences/editor_helper.rb +4 -1
- data/lib/audiences/engine.rb +19 -1
- data/lib/audiences/model.rb +9 -8
- data/lib/audiences/notifications.rb +4 -2
- data/lib/audiences/scim/field_mapping.rb +49 -0
- data/lib/audiences/scim/observer_base.rb +8 -0
- data/lib/audiences/scim/patch_groups_observer.rb +51 -0
- data/lib/audiences/scim/patch_op.rb +42 -0
- data/lib/audiences/scim/patch_users_observer.rb +38 -0
- data/lib/audiences/scim/scim_data.rb +31 -0
- data/lib/audiences/scim/upsert_groups_observer.rb +40 -0
- data/lib/audiences/scim/upsert_users_observer.rb +48 -0
- data/lib/audiences/scim.rb +9 -15
- data/lib/audiences/version.rb +1 -1
- data/lib/audiences.rb +5 -5
- metadata +40 -9
- data/app/jobs/audiences/application_job.rb +0 -6
- data/app/models/audiences/context_users.rb +0 -32
- data/app/models/audiences/criterion_users.rb +0 -28
- data/lib/audiences/railtie.rb +0 -12
- data/lib/audiences/scim/client.rb +0 -29
- data/lib/audiences/scim/resource.rb +0 -48
- data/lib/audiences/scim/resources_query.rb +0 -66
@@ -3,11 +3,11 @@
|
|
3
3
|
module Audiences
|
4
4
|
class ContextsController < ApplicationController
|
5
5
|
def show
|
6
|
-
|
6
|
+
render json: Audiences::Context.load(params.require(:key))
|
7
7
|
end
|
8
8
|
|
9
9
|
def update
|
10
|
-
|
10
|
+
render json: Audiences.update(params.require(:key), **context_params)
|
11
11
|
end
|
12
12
|
|
13
13
|
def users
|
@@ -17,7 +17,7 @@ module Audiences
|
|
17
17
|
limit: params[:limit],
|
18
18
|
offset: params[:offset])
|
19
19
|
|
20
|
-
render json: search
|
20
|
+
render json: search
|
21
21
|
end
|
22
22
|
|
23
23
|
private
|
@@ -32,22 +32,11 @@ module Audiences
|
|
32
32
|
@current_criterion ||= current_context.criteria.find(params[:criterion_id])
|
33
33
|
end
|
34
34
|
|
35
|
-
def render_context(context)
|
36
|
-
json_setting = {
|
37
|
-
only: %i[match_all],
|
38
|
-
methods: %i[count],
|
39
|
-
include: { criteria: { only: %i[id groups], methods: %i[count] } },
|
40
|
-
}
|
41
|
-
extra_users = context.extra_users.as_json(only: Audiences.exposed_user_attributes)
|
42
|
-
|
43
|
-
render json: { extra_users: extra_users, **context.as_json(json_setting) }
|
44
|
-
end
|
45
|
-
|
46
35
|
def context_params
|
47
36
|
params.permit(
|
48
37
|
:match_all,
|
49
38
|
criteria: [groups: {}],
|
50
|
-
extra_users: %i[externalId]
|
39
|
+
extra_users: %i[id externalId]
|
51
40
|
).to_h.symbolize_keys
|
52
41
|
end
|
53
42
|
end
|
@@ -3,23 +3,21 @@
|
|
3
3
|
module Audiences
|
4
4
|
class ScimProxyController < ApplicationController
|
5
5
|
def get
|
6
|
-
resources =
|
7
|
-
|
8
|
-
|
9
|
-
startIndex: params[:startIndex], count: params[:count],
|
10
|
-
attributes: Audiences.exposed_user_attributes.join(",")
|
11
|
-
)
|
6
|
+
resources = scope.search(params[:query])
|
7
|
+
.offset(params[:startIndex])
|
8
|
+
.limit(params[:count])
|
12
9
|
|
13
|
-
render json: resources
|
10
|
+
render json: resources
|
14
11
|
end
|
15
12
|
|
16
13
|
private
|
17
14
|
|
18
|
-
def
|
19
|
-
if params[:
|
20
|
-
|
15
|
+
def scope
|
16
|
+
if params[:scim_path].eql?("Users")
|
17
|
+
Audiences::ExternalUser.instance_exec(&Audiences.default_users_scope)
|
21
18
|
else
|
22
|
-
params[:
|
19
|
+
Audiences::Group.where(resource_type: params[:scim_path])
|
20
|
+
.instance_exec(&Audiences.default_groups_scope)
|
23
21
|
end
|
24
22
|
end
|
25
23
|
end
|
@@ -7,22 +7,59 @@ module Audiences
|
|
7
7
|
#
|
8
8
|
class Context < ApplicationRecord
|
9
9
|
include Locating
|
10
|
-
include ::Audiences::MembershipGroup
|
11
10
|
|
12
11
|
belongs_to :owner, polymorphic: true
|
13
12
|
has_many :criteria, class_name: "Audiences::Criterion",
|
14
13
|
autosave: true,
|
15
14
|
dependent: :destroy
|
16
15
|
|
16
|
+
has_many :context_extra_users, class_name: "Audiences::ContextExtraUser"
|
17
|
+
has_many :extra_users, class_name: "Audiences::ExternalUser",
|
18
|
+
through: :context_extra_users,
|
19
|
+
source: :external_user
|
20
|
+
|
21
|
+
scope :relevant_to, ->(group) do
|
22
|
+
joins(:criteria).merge(Criterion.relevant_to(group))
|
23
|
+
end
|
24
|
+
|
17
25
|
before_save if: :match_all do
|
18
26
|
self.criteria = []
|
19
27
|
self.extra_users = []
|
20
28
|
end
|
21
29
|
|
22
|
-
|
23
|
-
|
24
|
-
|
30
|
+
after_commit :notify_subscriptions, on: :update
|
31
|
+
|
32
|
+
def users
|
33
|
+
matching_external_users.instance_exec(&Audiences.default_users_scope)
|
34
|
+
end
|
35
|
+
|
36
|
+
delegate :count, to: :users
|
37
|
+
|
38
|
+
def as_json(...)
|
39
|
+
{
|
40
|
+
match_all: match_all,
|
41
|
+
count: count,
|
42
|
+
extra_users: extra_users.instance_exec(&Audiences.default_users_scope),
|
43
|
+
criteria: criteria,
|
44
|
+
}.as_json(...)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def notify_subscriptions
|
25
50
|
Notifications.publish(self)
|
26
51
|
end
|
52
|
+
|
53
|
+
def matching_external_users
|
54
|
+
match_all ? ExternalUser.all : matching_extra_users.or(matching_criteria)
|
55
|
+
end
|
56
|
+
|
57
|
+
def matching_extra_users
|
58
|
+
ExternalUser.where(id: extra_users.select(:id))
|
59
|
+
end
|
60
|
+
|
61
|
+
def matching_criteria
|
62
|
+
criteria.any? ? ExternalUser.matching_any(*criteria) : ExternalUser.none
|
63
|
+
end
|
27
64
|
end
|
28
65
|
end
|
@@ -2,19 +2,53 @@
|
|
2
2
|
|
3
3
|
module Audiences
|
4
4
|
class Criterion < ApplicationRecord
|
5
|
-
include ::Audiences::MembershipGroup
|
6
|
-
|
7
5
|
belongs_to :context, class_name: "Audiences::Context"
|
6
|
+
validates :groups, presence: true
|
7
|
+
|
8
|
+
has_many :criterion_groups, autosave: true, dependent: :destroy
|
9
|
+
has_many :groups, through: :criterion_groups
|
10
|
+
|
11
|
+
scope :relevant_to, ->(group) do
|
12
|
+
joins(:criterion_groups).where(criterion_groups: { group: group })
|
13
|
+
end
|
8
14
|
|
15
|
+
# Maps an array of attribute hashes to Criterion objects.
|
16
|
+
#
|
17
|
+
# Each attribute hash should have a :groups key, whose value is a hash
|
18
|
+
# mapping resource types (e.g., "Departments", "Territories") to arrays of group hashes,
|
19
|
+
# each containing an :id key (the SCIM group ID).
|
20
|
+
#
|
21
|
+
# Example input:
|
22
|
+
#
|
23
|
+
# [
|
24
|
+
# { groups: { Departments: [{ id: "1" }] } },
|
25
|
+
# { groups: { Territories: [{ id: "2" }], Departments: [{ id: "3" }] } },
|
26
|
+
# ]
|
27
|
+
#
|
28
|
+
# Returns an array of new Criterion objects, each initialized with the corresponding group criterion.
|
29
|
+
#
|
30
|
+
# @param [Array<Hash>] criteria Array of attribute hashes describing groups
|
31
|
+
# @return [Array<Criterion>] Array of Criterion objects
|
9
32
|
def self.map(criteria)
|
10
|
-
Array(criteria).map
|
33
|
+
Array(criteria).map do |attrs|
|
34
|
+
groups = attrs["groups"]&.flat_map do |resource_type, scim_groups|
|
35
|
+
Audiences::Group.from_scim(resource_type, *scim_groups).to_a
|
36
|
+
end
|
37
|
+
new(groups: groups)
|
38
|
+
end
|
11
39
|
end
|
12
40
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
41
|
+
def as_json(...)
|
42
|
+
groups = self.groups.group_by(&:resource_type)
|
43
|
+
|
44
|
+
{ id: id, count: count, groups: groups }.as_json(...)
|
45
|
+
end
|
46
|
+
|
47
|
+
def users
|
48
|
+
Audiences::ExternalUser.matching(self)
|
49
|
+
.instance_exec(&Audiences.default_users_scope)
|
18
50
|
end
|
51
|
+
|
52
|
+
delegate :count, to: :users
|
19
53
|
end
|
20
54
|
end
|
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
module Audiences
|
4
4
|
class ExternalUser < ApplicationRecord
|
5
|
+
has_many :group_memberships, dependent: :destroy
|
6
|
+
has_many :groups, through: :group_memberships, dependent: :destroy
|
7
|
+
|
8
|
+
has_many :context_extra_users, class_name: "Audiences::ContextExtraUser", dependent: :destroy
|
9
|
+
has_many :contexts, through: :context_extra_users, source: :context
|
10
|
+
|
5
11
|
if Audiences.config.identity_class
|
6
12
|
belongs_to :identity, class_name: Audiences.config.identity_class, # rubocop:disable Rails/ReflectionClassName
|
7
13
|
primary_key: Audiences.config.identity_key,
|
@@ -10,28 +16,100 @@ module Audiences
|
|
10
16
|
inverse_of: false
|
11
17
|
end
|
12
18
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
Array(external_ids).in_groups_of(count, false).flat_map do |ids|
|
17
|
-
filter = Array(ids).map { "externalId eq #{_1}" }.join(" OR ")
|
18
|
-
Audiences::Scim.resource(:Users).all(count: count, filter: filter).to_a
|
19
|
+
after_commit if: :active_previously_changed?, on: %i[create update destroy] do
|
20
|
+
group_contexts = groups.flat_map do |group|
|
21
|
+
Audiences::Context.relevant_to(group).to_a
|
19
22
|
end
|
23
|
+
match_all_contexts = Audiences::Context.where(match_all: true)
|
24
|
+
|
25
|
+
Audiences::Notifications.publish(*[*contexts, *group_contexts, *match_all_contexts].uniq)
|
26
|
+
end
|
27
|
+
|
28
|
+
scope :active, -> { where(active: true) }
|
29
|
+
|
30
|
+
scope :members_of, ->(groups) do
|
31
|
+
where(id: Audiences::GroupMembership.where(group: groups).select(:external_user_id))
|
32
|
+
end
|
33
|
+
|
34
|
+
scope :search, ->(display_name) do
|
35
|
+
where(arel_table[:display_name].matches("%#{display_name}%"))
|
20
36
|
end
|
21
37
|
|
22
|
-
|
23
|
-
|
38
|
+
scope :from_scim, ->(*scim_json) do
|
39
|
+
where(scim_id: scim_json.pluck("id").compact)
|
40
|
+
.or(where(user_id: scim_json.pluck("externalId").compact))
|
41
|
+
end
|
42
|
+
|
43
|
+
scope :matching, ->(criterion) do
|
44
|
+
return none if criterion.groups.empty?
|
24
45
|
|
25
|
-
|
26
|
-
|
46
|
+
criterion.groups
|
47
|
+
.group_by(&:resource_type)
|
48
|
+
.values
|
49
|
+
.reduce(self) do |scope, groups|
|
50
|
+
scope.members_of(groups)
|
27
51
|
end
|
28
|
-
|
29
|
-
|
30
|
-
|
52
|
+
end
|
53
|
+
|
54
|
+
scope :matching_any, ->(first, *others) do
|
55
|
+
others.reduce(matching(first)) do |scope, criterion|
|
56
|
+
scope.or matching(criterion)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def picture_urls = [picture_url]
|
61
|
+
|
62
|
+
def picture_urls=(urls)
|
63
|
+
self.picture_url = urls&.first
|
31
64
|
end
|
32
65
|
|
33
66
|
def as_json(...)
|
34
|
-
|
67
|
+
as_scim.slice(*Audiences.exposed_user_attributes)
|
35
68
|
end
|
69
|
+
|
70
|
+
def as_scim(...)
|
71
|
+
(data || {}).merge(groups_as_scim)
|
72
|
+
end
|
73
|
+
|
74
|
+
def groups_as_scim
|
75
|
+
names = groups.reduce({}) { |nam, group| nam.merge(group.resource_type => group.display_name) }
|
76
|
+
|
77
|
+
{
|
78
|
+
"groups" => groups.map { |g| { "value" => g.scim_id, "display" => g.display_name } },
|
79
|
+
"title" => names["Titles"],
|
80
|
+
"urn:ietf:params:scim:schemas:extension:authservice:2.0:User" => {
|
81
|
+
"role" => names["Roles"], "department" => names["Departments"],
|
82
|
+
"territory" => names["Territories"], "territoryAbbr" => TERRITORY_ABBRS[names["Territories"]]
|
83
|
+
},
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
TERRITORY_ABBRS = {
|
88
|
+
"Philadelphia" => "PHL",
|
89
|
+
"New Jersey" => "NJ",
|
90
|
+
"Maryland" => "MD",
|
91
|
+
"Connecticut" => "CT",
|
92
|
+
"Long Island" => "LI",
|
93
|
+
"Boston" => "BOS",
|
94
|
+
"Atlanta" => "ATL",
|
95
|
+
"Chicago" => "CHI",
|
96
|
+
"Detroit" => "DET",
|
97
|
+
"Houston" => "HOU",
|
98
|
+
"Dallas" => "DAL",
|
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
|
36
114
|
end
|
37
115
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Audiences
|
4
|
+
class Group < ApplicationRecord
|
5
|
+
has_many :group_memberships, dependent: :destroy
|
6
|
+
has_many :external_users, through: :group_memberships, dependent: :destroy
|
7
|
+
|
8
|
+
validates :display_name, presence: true
|
9
|
+
validates :external_id, presence: true
|
10
|
+
validates :scim_id, presence: true
|
11
|
+
|
12
|
+
scope :active, -> { where(active: true) }
|
13
|
+
|
14
|
+
scope :search, ->(display_name) do
|
15
|
+
where(arel_table[:display_name].matches("%#{display_name}%"))
|
16
|
+
end
|
17
|
+
|
18
|
+
scope :from_scim, ->(resource_type, *scim_json) do
|
19
|
+
where(scim_id: scim_json.pluck("id"))
|
20
|
+
.or(where(resource_type: resource_type, external_id: scim_json.pluck("externalId")))
|
21
|
+
end
|
22
|
+
|
23
|
+
def as_json(...)
|
24
|
+
{ "id" => scim_id, "externalId" => external_id, "displayName" => display_name }.as_json(...)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Audiences
|
4
|
+
class GroupMembership < ApplicationRecord
|
5
|
+
belongs_to :external_user
|
6
|
+
belongs_to :group
|
7
|
+
|
8
|
+
after_commit on: %i[create destroy] do
|
9
|
+
relevant_groups = Audiences::Context.relevant_to(group)
|
10
|
+
|
11
|
+
Audiences::Notifications.publish(*relevant_groups.to_a)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateAudiencesGroups < ActiveRecord::Migration[6.1]
|
4
|
+
def change
|
5
|
+
create_table :audiences_groups do |t|
|
6
|
+
t.string :external_id
|
7
|
+
t.string :scim_id
|
8
|
+
t.string :display_name
|
9
|
+
t.string :resource_type
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
|
13
|
+
t.index %i[resource_type external_id], unique: true
|
14
|
+
t.index %i[resource_type scim_id], unique: true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateAudiencesGroupMemberships < ActiveRecord::Migration[6.1]
|
4
|
+
def change
|
5
|
+
create_table :audiences_group_memberships do |t|
|
6
|
+
t.belongs_to :external_user, null: false, foreign_key: false, table: :audiences_external_users
|
7
|
+
t.belongs_to :group, null: false, foreign_key: false, table: :audiences_external_users
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class PopulateExternalUsersScimId < ActiveRecord::Migration[6.1]
|
4
|
+
def down; end
|
5
|
+
|
6
|
+
def up
|
7
|
+
Audiences::ExternalUser.unscoped.find_each do |user|
|
8
|
+
user.update!(
|
9
|
+
scim_id: user.data&.fetch("id", nil),
|
10
|
+
display_name: user.data&.fetch("displayName", nil),
|
11
|
+
picture_urls: user.data&.fetch("photos", [])&.pluck("value")
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class AddActiveFlagToExternalUsersAndGroups < ActiveRecord::Migration[6.1]
|
4
|
+
def change
|
5
|
+
add_column :audiences_external_users, :active, :boolean, default: true, null: false
|
6
|
+
add_column :audiences_groups, :active, :boolean, default: true, null: false
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateAudiencesCriterionGroups < ActiveRecord::Migration[6.1]
|
4
|
+
def change
|
5
|
+
create_table :audiences_criterion_groups do |t|
|
6
|
+
t.references :criterion, foreign_key: false
|
7
|
+
t.references :group, foreign_key: false
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MoveGroupCriterionToCriterionGroups < ActiveRecord::Migration[6.1]
|
4
|
+
def up
|
5
|
+
Audiences::Criterion.find_each do |criterion|
|
6
|
+
next if criterion.groups_json.blank?
|
7
|
+
|
8
|
+
groups = criterion.groups_json.values.flat_map do |scim_groups|
|
9
|
+
scim_groups.pluck("id")
|
10
|
+
end
|
11
|
+
|
12
|
+
criterion.update!(groups: Audiences::Group.where(scim_id: groups))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def down
|
17
|
+
Audiences::Criterion.find_each do |criterion|
|
18
|
+
next if criterion.groups.blank?
|
19
|
+
|
20
|
+
groups_json = criterion.groups.group_by(&:resource_type).transform_values do |groups|
|
21
|
+
groups.map(&:as_json)
|
22
|
+
end
|
23
|
+
|
24
|
+
criterion.update!(groups_json: groups_json)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateAudiencesContextExtraUsers < ActiveRecord::Migration[6.1]
|
4
|
+
def change
|
5
|
+
create_table :audiences_context_extra_users do |t|
|
6
|
+
t.references :external_user, foreign_key: false
|
7
|
+
t.references :context, foreign_key: false
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MoveExtraUsersToContextExtraUsers < ActiveRecord::Migration[6.1]
|
4
|
+
def up
|
5
|
+
Audiences::Context.find_each do |context|
|
6
|
+
next if context.extra_users_json.blank?
|
7
|
+
|
8
|
+
users = Audiences::ExternalUser.from_scim(*context.extra_users_json)
|
9
|
+
|
10
|
+
context.update!(extra_users: users)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def down
|
15
|
+
Audiences::Context.find_each do |context|
|
16
|
+
next if context.extra_users.blank?
|
17
|
+
|
18
|
+
context.update!(extra_users_json: context.extra_users.map(&:as_json))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/docs/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# Version 2.0 (2025-08-25)
|
4
|
+
|
5
|
+
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.
|
6
|
+
|
7
|
+
- Update ExternalUser on membership changeas [#541](https://github.com/powerhome/audiences/pull/541)
|
8
|
+
- Filter extra_users in context JSON [#540](https://github.com/powerhome/audiences/pull/540)
|
9
|
+
- Introduce helpful scopes [#538](https://github.com/powerhome/audiences/pull/538)
|
10
|
+
- Allow inactive groups in criteria [#537](https://github.com/powerhome/audiences/pull/537)
|
11
|
+
- Allow creating external users and groups with internal and external ids [#536](https://github.com/powerhome/audiences/pull/536)
|
12
|
+
- Reactive audience recalculation [#535](https://github.com/powerhome/audiences/pull/535)
|
13
|
+
- match group criteria [#534](https://github.com/powerhome/audiences/pull/534)
|
14
|
+
- Configurable default user and groups scope [#533](https://github.com/powerhome/audiences/pull/533)
|
15
|
+
- Default authentication to reject requests [#532](https://github.com/powerhome/audiences/pull/532)
|
16
|
+
- scim proxy with local data [#531](https://github.com/powerhome/audiences/pull/531)
|
17
|
+
- Local audience calculations [#530](https://github.com/powerhome/audiences/pull/530)
|
18
|
+
- Local user and group info updated with TwoPercent [#519](https://github.com/powerhome/audiences/pull/519)
|
19
|
+
|
3
20
|
# Version 1.6 (2025-03-19)
|
4
21
|
|
5
22
|
- Prepackaged audiences-react in UJS [#510](https://github.com/powerhome/audiences/pull/510)
|
data/docs/README.md
CHANGED
@@ -42,17 +42,6 @@ For more details, refer to [editor_helper](../lib/audiences/editor_helper.rb).
|
|
42
42
|
|
43
43
|
### Configuring Audiences
|
44
44
|
|
45
|
-
The `Audience.config.scim` should point to the SCIM endpoint. Configure the endpoint and the credentials/headers as follows:
|
46
|
-
|
47
|
-
```ruby
|
48
|
-
Audiences.configure do |config|
|
49
|
-
config.scim = {
|
50
|
-
uri: ENV.fetch("SCIM_V2_API"),
|
51
|
-
headers: { "Authorization" => "Bearer #{ENV.fetch('SCIM_V2_TOKEN')}" }
|
52
|
-
}
|
53
|
-
end
|
54
|
-
```
|
55
|
-
|
56
45
|
### Adding Audiences to a Model
|
57
46
|
|
58
47
|
A model object can contain multiple audience contexts using the `has_audience` module helper, which is added to ActiveRecord automatically when configured:
|