open-loam 0.2.0 → 0.3.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/CHANGELOG.md +116 -0
- data/README.md +4 -2
- data/app/jobs/open_loam/event_log_prune_job.rb +15 -0
- data/app/jobs/open_loam/webhook_delivery_job.rb +10 -1
- data/app/models/open_loam/api_token.rb +27 -4
- data/app/models/open_loam/event_record.rb +42 -0
- data/app/models/open_loam/pending_action.rb +47 -3
- data/app/models/open_loam/sso_provider.rb +31 -0
- data/app/models/open_loam/webhook_endpoint.rb +14 -1
- data/app/views/open_loam/custom_fields/_fields.html.erb +4 -0
- data/lib/generators/open_loam/entity/templates/api_controller.rb +1 -0
- data/lib/generators/open_loam/entity/templates/controller.rb +11 -1
- data/lib/generators/open_loam/entity/templates/views/_form.html.erb +4 -1
- data/lib/generators/open_loam/entity/templates/views/index.html.erb +6 -2
- data/lib/generators/open_loam/entity/templates/views/show.html.erb +2 -0
- data/lib/generators/open_loam/install/install_generator.rb +11 -0
- data/lib/generators/open_loam/install/templates/AGENTS.md +5 -1
- data/lib/generators/open_loam/install/templates/admin/api_tokens_controller.rb +25 -7
- data/lib/generators/open_loam/install/templates/admin/api_tokens_index.html.erb +23 -0
- data/lib/generators/open_loam/install/templates/admin/base_controller.rb +63 -0
- data/lib/generators/open_loam/install/templates/admin/dashboard_controller.rb +2 -0
- data/lib/generators/open_loam/install/templates/admin/events_controller.rb +2 -0
- data/lib/generators/open_loam/install/templates/admin/history_controller.rb +26 -0
- data/lib/generators/open_loam/install/templates/admin/history_index.html.erb +1 -1
- data/lib/generators/open_loam/install/templates/admin/imports_controller.rb +13 -1
- data/lib/generators/open_loam/install/templates/admin/inbound_webhook_sources_controller.rb +1 -1
- data/lib/generators/open_loam/install/templates/admin/inbound_webhook_sources_new.html.erb +0 -2
- data/lib/generators/open_loam/install/templates/admin/mfa_controller.rb +5 -0
- data/lib/generators/open_loam/install/templates/admin/notifications_controller.rb +2 -0
- data/lib/generators/open_loam/install/templates/admin/perspectives_controller.rb +6 -0
- data/lib/generators/open_loam/install/templates/admin/progress_jobs_controller.rb +4 -0
- data/lib/generators/open_loam/install/templates/admin/search_controller.rb +2 -0
- data/lib/generators/open_loam/install/templates/admin/sessions_controller.rb +3 -4
- data/lib/generators/open_loam/install/templates/admin/sso_providers_index.html.erb +8 -1
- data/lib/generators/open_loam/install/templates/admin/sudo_controller.rb +3 -1
- data/lib/generators/open_loam/install/templates/api_base_controller.rb +43 -6
- data/lib/generators/open_loam/install/templates/import_job.rb +14 -1
- data/lib/generators/open_loam/install/templates/initializer.rb +14 -2
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_api_tokens.rb +2 -2
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_event_records.rb +13 -0
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_inbound_webhooks.rb +0 -1
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_sso_providers.rb +3 -0
- data/lib/open_loam/auth_throttle.rb +11 -3
- data/lib/open_loam/custom_field_index.rb +11 -8
- data/lib/open_loam/encryptable.rb +8 -3
- data/lib/open_loam/encryption/key_provider.rb +17 -1
- data/lib/open_loam/encryption.rb +47 -4
- data/lib/open_loam/engine.rb +8 -0
- data/lib/open_loam/errors.rb +10 -0
- data/lib/open_loam/event_log.rb +77 -0
- data/lib/open_loam/export.rb +6 -6
- data/lib/open_loam/import.rb +1 -4
- data/lib/open_loam/inbound_webhooks.rb +12 -11
- data/lib/open_loam/lifecycle.rb +29 -0
- data/lib/open_loam/mcp.rb +10 -1
- data/lib/open_loam/outbound_url.rb +100 -0
- data/lib/open_loam/policy.rb +16 -3
- data/lib/open_loam/search/token_driver.rb +1 -1
- data/lib/open_loam/sso/http_client.rb +15 -6
- data/lib/open_loam/sso.rb +23 -2
- data/lib/open_loam/version.rb +1 -1
- data/lib/open_loam.rb +2 -0
- data/lib/tasks/open_loam.rake +32 -0
- metadata +6 -1
data/lib/open_loam/sso.rb
CHANGED
|
@@ -32,6 +32,11 @@ module OpenLoam
|
|
|
32
32
|
# (cross-domain account takeover), so the flow refuses it.
|
|
33
33
|
class DomainMismatchError < Error; end
|
|
34
34
|
|
|
35
|
+
# The provider has not proven it owns its `domain`, so it may not adopt an
|
|
36
|
+
# existing account. Ownership is stamped out of band by an operator
|
|
37
|
+
# (rake open_loam:sso:verify_domain).
|
|
38
|
+
class UnverifiedDomainError < Error; end
|
|
39
|
+
|
|
35
40
|
class << self
|
|
36
41
|
attr_writer :builder
|
|
37
42
|
|
|
@@ -60,11 +65,14 @@ module OpenLoam
|
|
|
60
65
|
# OpenLoam::Membership.tenants_for): "which tenant's IdP owns this email
|
|
61
66
|
# domain?" is asked at the sign-in page, before any tenant is chosen, so it
|
|
62
67
|
# reaches across tenants via `unscoped` — something host code must never do.
|
|
68
|
+
#
|
|
69
|
+
# Verified providers only: the domain is manager-typed, so an unverified
|
|
70
|
+
# claim would capture every sign-in on a domain the tenant does not own.
|
|
63
71
|
def provider_for(email:)
|
|
64
72
|
domain = email.to_s.split("@").last.to_s.strip.downcase
|
|
65
73
|
return nil if domain.blank?
|
|
66
74
|
|
|
67
|
-
OpenLoam::SsoProvider.unscoped.where(domain: domain, active: true).first
|
|
75
|
+
OpenLoam::SsoProvider.unscoped.domain_verified.where(domain: domain, active: true).first
|
|
68
76
|
end
|
|
69
77
|
|
|
70
78
|
# Resolve verified IdP claims to a signed-in User, in the provider's tenant.
|
|
@@ -87,7 +95,7 @@ module OpenLoam
|
|
|
87
95
|
# Resolve the user: the durable (provider, sub) identity first, then an
|
|
88
96
|
# existing User by verified email (link), else just-in-time create.
|
|
89
97
|
identity = OpenLoam::SsoIdentity.find_by(sso_provider_id: provider.id, sub: claims.sub)
|
|
90
|
-
user = identity&.user ||
|
|
98
|
+
user = identity&.user || link_or_create_user(provider, claims)
|
|
91
99
|
|
|
92
100
|
# Re-map claims -> role on EVERY login (including a returning identity),
|
|
93
101
|
# so an IdP role change takes effect.
|
|
@@ -106,6 +114,19 @@ module OpenLoam
|
|
|
106
114
|
|
|
107
115
|
private
|
|
108
116
|
|
|
117
|
+
# `User` is global, so linking adopts an account this tenant did not create
|
|
118
|
+
# — allowed only once the provider proved it owns the domain. JIT-creating
|
|
119
|
+
# needs no proof: the new user lands in the claimant's own tenant.
|
|
120
|
+
def link_or_create_user(provider, claims)
|
|
121
|
+
existing = User.find_by(email: claims.email)
|
|
122
|
+
return jit_create_user(claims) if existing.nil?
|
|
123
|
+
return existing if provider.domain_verified?
|
|
124
|
+
|
|
125
|
+
raise UnverifiedDomainError,
|
|
126
|
+
"provider for #{provider.domain.inspect} has not proven it owns that domain — " \
|
|
127
|
+
"refusing to link the existing account #{claims.email.inspect}"
|
|
128
|
+
end
|
|
129
|
+
|
|
109
130
|
def jit_create_user(claims)
|
|
110
131
|
# SSO users authenticate at the IdP; the random password satisfies
|
|
111
132
|
# has_secure_password and is unguessable, never used to log in.
|
data/lib/open_loam/version.rb
CHANGED
data/lib/open_loam.rb
CHANGED
|
@@ -7,6 +7,7 @@ require "open_loam/lifecycle"
|
|
|
7
7
|
require "open_loam/configs"
|
|
8
8
|
require "open_loam/features"
|
|
9
9
|
require "open_loam/auth_throttle"
|
|
10
|
+
require "open_loam/outbound_url"
|
|
10
11
|
require "open_loam/dictionaries"
|
|
11
12
|
require "open_loam/custom_field_index"
|
|
12
13
|
require "open_loam/undo"
|
|
@@ -44,6 +45,7 @@ require "open_loam/business_rules"
|
|
|
44
45
|
require "open_loam/webhooks"
|
|
45
46
|
require "open_loam/inbound_webhooks"
|
|
46
47
|
require "open_loam/durable_events"
|
|
48
|
+
require "open_loam/event_log"
|
|
47
49
|
require "open_loam/engine" if defined?(Rails::Engine)
|
|
48
50
|
|
|
49
51
|
module OpenLoam
|
data/lib/tasks/open_loam.rake
CHANGED
|
@@ -103,6 +103,38 @@ namespace :open_loam do
|
|
|
103
103
|
end
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
+
namespace :sso do
|
|
107
|
+
# Domain ownership can only be granted from OUTSIDE the tenant: the manager
|
|
108
|
+
# who typed the domain is the party this check exists to constrain. Confirm
|
|
109
|
+
# ownership out of band (DNS TXT, a signed request from the domain's
|
|
110
|
+
# operator, an existing contract) BEFORE running this.
|
|
111
|
+
#
|
|
112
|
+
# bin/rails open_loam:sso:verify_domain[42]
|
|
113
|
+
desc "Mark an SSO provider's domain as proven-owned (operator only)"
|
|
114
|
+
task :verify_domain, %i[provider_id] => :environment do |_task, args|
|
|
115
|
+
provider = OpenLoam::SsoProvider.unscoped.find(args[:provider_id])
|
|
116
|
+
tenant = OpenLoam::Tenant.find(provider.tenant_id)
|
|
117
|
+
OpenLoam.as_tenant(tenant) { provider.verify_domain! }
|
|
118
|
+
puts "open_loam:sso:verify_domain — #{provider.domain} verified for tenant #{tenant.slug} (provider #{provider.id})."
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
desc "Revoke an SSO provider's domain verification"
|
|
122
|
+
task :unverify_domain, %i[provider_id] => :environment do |_task, args|
|
|
123
|
+
provider = OpenLoam::SsoProvider.unscoped.find(args[:provider_id])
|
|
124
|
+
tenant = OpenLoam::Tenant.find(provider.tenant_id)
|
|
125
|
+
OpenLoam.as_tenant(tenant) { provider.update!(domain_verified_at: nil) }
|
|
126
|
+
puts "open_loam:sso:unverify_domain — #{provider.domain} is no longer verified."
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
desc "List SSO providers and their domain-verification state"
|
|
130
|
+
task providers: :environment do
|
|
131
|
+
OpenLoam::SsoProvider.unscoped.order(:domain).each do |provider|
|
|
132
|
+
state = provider.domain_verified? ? "verified #{provider.domain_verified_at.to_fs(:db)}" : "UNVERIFIED"
|
|
133
|
+
puts format("%-6s %-30s tenant=%-6s %s", provider.id, provider.domain, provider.tenant_id, state)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
106
138
|
namespace :encryption do
|
|
107
139
|
# Rotate a tenant's encrypted data under the current key: read each record's
|
|
108
140
|
# encrypted fields (old key) and re-seal them (new key), one record at a
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: open-loam
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Grzegorz Smajdor
|
|
@@ -64,6 +64,7 @@ files:
|
|
|
64
64
|
- README.md
|
|
65
65
|
- app/jobs/open_loam/custom_field_reindex_job.rb
|
|
66
66
|
- app/jobs/open_loam/event_delivery_job.rb
|
|
67
|
+
- app/jobs/open_loam/event_log_prune_job.rb
|
|
67
68
|
- app/jobs/open_loam/event_redelivery_sweep_job.rb
|
|
68
69
|
- app/jobs/open_loam/webhook_delivery_job.rb
|
|
69
70
|
- app/models/open_loam/api_token.rb
|
|
@@ -78,6 +79,7 @@ files:
|
|
|
78
79
|
- app/models/open_loam/dictionary.rb
|
|
79
80
|
- app/models/open_loam/dictionary_entry.rb
|
|
80
81
|
- app/models/open_loam/event_delivery.rb
|
|
82
|
+
- app/models/open_loam/event_record.rb
|
|
81
83
|
- app/models/open_loam/field_definition.rb
|
|
82
84
|
- app/models/open_loam/inbound_webhook_delivery.rb
|
|
83
85
|
- app/models/open_loam/inbound_webhook_source.rb
|
|
@@ -210,6 +212,7 @@ files:
|
|
|
210
212
|
- lib/generators/open_loam/install/templates/migrations/create_open_loam_dictionaries.rb
|
|
211
213
|
- lib/generators/open_loam/install/templates/migrations/create_open_loam_dictionary_entries.rb
|
|
212
214
|
- lib/generators/open_loam/install/templates/migrations/create_open_loam_event_deliveries.rb
|
|
215
|
+
- lib/generators/open_loam/install/templates/migrations/create_open_loam_event_records.rb
|
|
213
216
|
- lib/generators/open_loam/install/templates/migrations/create_open_loam_field_definitions.rb
|
|
214
217
|
- lib/generators/open_loam/install/templates/migrations/create_open_loam_inbound_webhooks.rb
|
|
215
218
|
- lib/generators/open_loam/install/templates/migrations/create_open_loam_memberships.rb
|
|
@@ -256,6 +259,7 @@ files:
|
|
|
256
259
|
- lib/open_loam/enrichers.rb
|
|
257
260
|
- lib/open_loam/errors.rb
|
|
258
261
|
- lib/open_loam/eval.rb
|
|
262
|
+
- lib/open_loam/event_log.rb
|
|
259
263
|
- lib/open_loam/event_stream.rb
|
|
260
264
|
- lib/open_loam/eventful.rb
|
|
261
265
|
- lib/open_loam/events.rb
|
|
@@ -270,6 +274,7 @@ files:
|
|
|
270
274
|
- lib/open_loam/mcp/server.rb
|
|
271
275
|
- lib/open_loam/notifications.rb
|
|
272
276
|
- lib/open_loam/open_api.rb
|
|
277
|
+
- lib/open_loam/outbound_url.rb
|
|
273
278
|
- lib/open_loam/overrides.rb
|
|
274
279
|
- lib/open_loam/pending_actions.rb
|
|
275
280
|
- lib/open_loam/permissions.rb
|