masks-server 0.7.0 → 0.9.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/controllers/concerns/masks/server/client_authentication.rb +1 -1
- data/app/controllers/concerns/masks/server/resource_token.rb +12 -3
- data/app/controllers/masks/server/application_controller.rb +8 -6
- data/app/controllers/masks/server/authorize_controller.rb +1 -1
- data/app/controllers/masks/server/enrollments_controller.rb +66 -0
- data/app/controllers/masks/server/introspections_controller.rb +2 -2
- data/app/controllers/masks/server/logins_controller.rb +2 -1
- data/app/controllers/masks/server/registrations_controller.rb +40 -2
- data/app/controllers/masks/server/scim/metadata_controller.rb +1 -1
- data/app/controllers/masks/server/tokens_controller.rb +1 -1
- data/app/graphql/masks/server/manage/mutations/add_app.rb +1 -1
- data/app/graphql/masks/server/manage/types/client_type.rb +8 -0
- data/app/graphql/masks/server/manage/types/query_type.rb +6 -1
- data/app/helpers/masks/server/account_helper.rb +1 -1
- data/app/helpers/masks/server/application_helper.rb +0 -1
- data/app/helpers/masks/server/scopes_helper.rb +28 -0
- data/app/jobs/masks/server/cleanup_job.rb +1 -0
- data/app/models/masks/server/actor.rb +3 -3
- data/app/models/masks/server/client.rb +127 -7
- data/app/models/masks/server/client_keys.rb +6 -1
- data/app/models/masks/server/enrollment_token.rb +54 -0
- data/app/models/masks/server/event.rb +3 -0
- data/app/models/masks/server/handshake.rb +12 -6
- data/app/models/masks/server/issuer.rb +1 -0
- data/app/models/masks/server/login.rb +3 -6
- data/app/models/masks/server/namespace.rb +6 -3
- data/app/models/masks/server/pending_handshake.rb +2 -1
- data/app/models/masks/server/replay.rb +22 -4
- data/app/models/masks/server/session.rb +7 -5
- data/app/models/masks/server/token.rb +1 -0
- data/app/policies/masks/server/exchange_policy.rb +1 -1
- data/app/policies/masks/server/grant_checks.rb +1 -1
- data/app/views/layouts/masks/server/manage.html.erb +2 -2
- data/app/views/masks/server/account/_info.html.erb +1 -1
- data/app/views/masks/server/account/_organizations.html.erb +4 -4
- data/app/views/masks/server/application/_actor.html.erb +27 -0
- data/app/views/masks/server/handshakes/refused.html.erb +1 -1
- data/app/views/masks/server/handshakes/show.html.erb +129 -59
- data/app/views/masks/server/logins/_choose_organization.html.erb +1 -1
- data/app/views/masks/server/logins/_consent.html.erb +1 -1
- data/app/views/masks/server/sessions/confirm.html.erb +1 -1
- data/config/locales/de/account.yml +3 -3
- data/config/locales/de/application.yml +1 -1
- data/config/locales/de/events.yml +10 -7
- data/config/locales/de/flashes.yml +2 -2
- data/config/locales/de/handshakes.yml +33 -14
- data/config/locales/de/mailers.yml +1 -1
- data/config/locales/de/manage/activity.yml +2 -2
- data/config/locales/de/manage/actor.yml +41 -23
- data/config/locales/de/manage/actors.yml +16 -15
- data/config/locales/de/manage/api.yml +1 -1
- data/config/locales/de/manage/app.yml +6 -6
- data/config/locales/de/manage/client.yml +45 -45
- data/config/locales/de/manage/clients.yml +28 -32
- data/config/locales/de/manage/connections.yml +2 -2
- data/config/locales/de/manage/consents.yml +2 -2
- data/config/locales/de/manage/device.yml +7 -7
- data/config/locales/de/manage/devices.yml +18 -18
- data/config/locales/de/manage/directory.yml +1 -1
- data/config/locales/de/manage/domains.yml +6 -6
- data/config/locales/de/manage/emails.yml +4 -4
- data/config/locales/de/manage/events.yml +3 -0
- data/config/locales/de/manage/groups.yml +7 -7
- data/config/locales/de/manage/ldap.yml +8 -8
- data/config/locales/de/manage/namespaces.yml +1 -1
- data/config/locales/de/manage/organization.yml +15 -8
- data/config/locales/de/manage/organizations.yml +3 -3
- data/config/locales/de/manage/policies.yml +6 -6
- data/config/locales/de/manage/policy_rules.yml +1 -1
- data/config/locales/de/manage/presence.yml +6 -6
- data/config/locales/de/manage/providers.yml +70 -48
- data/config/locales/de/manage/provisioning.yml +4 -4
- data/config/locales/de/manage/senders.yml +1 -1
- data/config/locales/de/manage/settings.yml +37 -26
- data/config/locales/de/manage/settings_shell.yml +2 -2
- data/config/locales/de/refusals.yml +5 -5
- data/config/locales/en/account.yml +5 -5
- data/config/locales/en/application.yml +1 -1
- data/config/locales/en/events.yml +9 -6
- data/config/locales/en/flashes.yml +3 -3
- data/config/locales/en/handshakes.yml +33 -14
- data/config/locales/en/logins.yml +2 -2
- data/config/locales/en/manage/actor.yml +32 -14
- data/config/locales/en/manage/actors.yml +16 -15
- data/config/locales/en/manage/app.yml +5 -5
- data/config/locales/en/manage/client.yml +9 -9
- data/config/locales/en/manage/clients.yml +8 -12
- data/config/locales/en/manage/connections.yml +1 -1
- data/config/locales/en/manage/consents.yml +1 -1
- data/config/locales/en/manage/device.yml +1 -1
- data/config/locales/en/manage/devices.yml +1 -1
- data/config/locales/en/manage/directory.yml +1 -1
- data/config/locales/en/manage/domains.yml +6 -6
- data/config/locales/en/manage/emails.yml +4 -4
- data/config/locales/en/manage/events.yml +3 -0
- data/config/locales/en/manage/groups.yml +7 -7
- data/config/locales/en/manage/ldap.yml +8 -8
- data/config/locales/en/manage/messages.yml +1 -1
- data/config/locales/en/manage/namespaces.yml +1 -1
- data/config/locales/en/manage/organization.yml +16 -9
- data/config/locales/en/manage/organizations.yml +3 -3
- data/config/locales/en/manage/policies.yml +6 -6
- data/config/locales/en/manage/policy_rules.yml +2 -2
- data/config/locales/en/manage/presence.yml +1 -1
- data/config/locales/en/manage/providers.yml +70 -48
- data/config/locales/en/manage/provisioning.yml +4 -4
- data/config/locales/en/manage/senders.yml +2 -2
- data/config/locales/en/manage/settings.yml +34 -23
- data/config/locales/es/account.yml +3 -3
- data/config/locales/es/application.yml +2 -2
- data/config/locales/es/events.yml +9 -6
- data/config/locales/es/flashes.yml +4 -4
- data/config/locales/es/handshakes.yml +33 -14
- data/config/locales/es/logins.yml +12 -12
- data/config/locales/es/mailers.yml +7 -7
- data/config/locales/es/manage/activity.yml +2 -2
- data/config/locales/es/manage/actor.yml +33 -15
- data/config/locales/es/manage/actors.yml +17 -16
- data/config/locales/es/manage/api.yml +1 -1
- data/config/locales/es/manage/app.yml +6 -6
- data/config/locales/es/manage/client.yml +30 -30
- data/config/locales/es/manage/clients.yml +22 -26
- data/config/locales/es/manage/connections.yml +1 -1
- data/config/locales/es/manage/consents.yml +1 -1
- data/config/locales/es/manage/device.yml +1 -1
- data/config/locales/es/manage/directory.yml +1 -1
- data/config/locales/es/manage/domains.yml +5 -5
- data/config/locales/es/manage/emails.yml +7 -7
- data/config/locales/es/manage/events.yml +3 -0
- data/config/locales/es/manage/groups.yml +7 -7
- data/config/locales/es/manage/ldap.yml +8 -8
- data/config/locales/es/manage/namespaces.yml +1 -1
- data/config/locales/es/manage/organization.yml +16 -9
- data/config/locales/es/manage/organizations.yml +3 -3
- data/config/locales/es/manage/policies.yml +8 -8
- data/config/locales/es/manage/policy_rules.yml +2 -2
- data/config/locales/es/manage/providers.yml +70 -48
- data/config/locales/es/manage/provisioning.yml +4 -4
- data/config/locales/es/manage/senders.yml +1 -1
- data/config/locales/es/manage/settings.yml +38 -27
- data/config/locales/es/manage/settings_shell.yml +2 -2
- data/config/locales/es/refusals.yml +3 -3
- data/config/locales/fr/account.yml +2 -2
- data/config/locales/fr/application.yml +1 -1
- data/config/locales/fr/events.yml +8 -5
- data/config/locales/fr/flashes.yml +1 -1
- data/config/locales/fr/handshakes.yml +33 -14
- data/config/locales/fr/manage/activity.yml +4 -4
- data/config/locales/fr/manage/actor.yml +43 -25
- data/config/locales/fr/manage/actors.yml +22 -21
- data/config/locales/fr/manage/api.yml +1 -1
- data/config/locales/fr/manage/app.yml +7 -7
- data/config/locales/fr/manage/client.yml +50 -50
- data/config/locales/fr/manage/clients.yml +28 -32
- data/config/locales/fr/manage/connections.yml +2 -2
- data/config/locales/fr/manage/consents.yml +1 -1
- data/config/locales/fr/manage/device.yml +1 -1
- data/config/locales/fr/manage/directory.yml +1 -1
- data/config/locales/fr/manage/domains.yml +6 -6
- data/config/locales/fr/manage/emails.yml +5 -5
- data/config/locales/fr/manage/events.yml +3 -0
- data/config/locales/fr/manage/groups.yml +7 -7
- data/config/locales/fr/manage/ldap.yml +8 -8
- data/config/locales/fr/manage/namespaces.yml +1 -1
- data/config/locales/fr/manage/organization.yml +15 -8
- data/config/locales/fr/manage/organizations.yml +3 -3
- data/config/locales/fr/manage/policies.yml +6 -6
- data/config/locales/fr/manage/policy_rules.yml +1 -1
- data/config/locales/fr/manage/providers.yml +70 -48
- data/config/locales/fr/manage/provisioning.yml +4 -4
- data/config/locales/fr/manage/senders.yml +1 -1
- data/config/locales/fr/manage/settings.yml +38 -27
- data/config/locales/fr/manage/settings_shell.yml +2 -2
- data/config/routes.rb +2 -0
- data/config/vite.json +1 -0
- data/db/migrate/20261011190000_add_enrolled_by_to_clients.rb +5 -0
- data/db/migrate/20261012100000_create_replays.rb +23 -0
- data/db/migrate/20261013010000_add_handshake_return_to_to_clients.rb +5 -0
- data/lib/generators/masks/server/install/install_generator.rb +2 -2
- data/lib/masks/server/browser_access.rb +35 -0
- data/lib/masks/server/configuration.rb +3 -3
- data/lib/masks/server/engine.rb +1 -0
- data/lib/masks/server/schema_reference.rb +17 -4
- data/lib/masks/server.rb +1 -0
- data/public/masks-assets/.vite/manifest-assets.json +1 -0
- data/public/masks-assets/.vite/manifest.json +62 -0
- data/public/masks-assets/assets/app-B27oPUll.css +2 -0
- data/public/masks-assets/assets/console-DoyNbIw3.css +1 -0
- data/public/masks-assets/assets/handshake-DIFZPSAK.js +2 -0
- data/public/masks-assets/assets/handshake-DIFZPSAK.js.map +1 -0
- data/public/masks-assets/assets/{initial-D99AzFOd.js → initial-C1GpCiEh.js} +2 -2
- data/public/masks-assets/assets/{initial-D99AzFOd.js.map → initial-C1GpCiEh.js.map} +1 -1
- data/public/masks-assets/assets/login-C_6igRZ3.js +4 -0
- data/public/masks-assets/assets/login-C_6igRZ3.js.map +1 -0
- data/public/masks-assets/assets/manage-Dbfiy3Mj.js +567 -0
- data/public/masks-assets/assets/manage-Dbfiy3Mj.js.map +1 -0
- data/version.txt +1 -1
- metadata +21 -12
- data/app/views/masks/server/application/_person.html.erb +0 -27
- data/public/masks-assets/assets/app-olx-5PkA.css +0 -2
- data/public/masks-assets/assets/console-CiuQ3qj0.css +0 -1
- data/public/masks-assets/assets/handshake-BYhhQ_X3.js +0 -2
- data/public/masks-assets/assets/handshake-BYhhQ_X3.js.map +0 -1
- data/public/masks-assets/assets/login-CNs1UpxJ.js +0 -4
- data/public/masks-assets/assets/login-CNs1UpxJ.js.map +0 -1
- data/public/masks-assets/assets/manage-Z3GmPDNQ.js +0 -560
- data/public/masks-assets/assets/manage-Z3GmPDNQ.js.map +0 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 36e33f34e755bbbb4470a5c55b920624f2042678b87c810ce614e86f9638a4df
|
|
4
|
+
data.tar.gz: 2bf178807ba578015922b44145ad0eb28454698adb877ac51b5059be57a35ca3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3d32dd6592c56c0132971c1f322ca90b2a31df6832c0b9fe33d5c41c7d5d7aab443b4c6b2fec0a9d3ac0635b22b6d86bbda1c451bd791fd3794e4776c1edd1c8
|
|
7
|
+
data.tar.gz: 59f5658c277590947cf74f3b8e57fe2d9d7b01f1a160bdc3bdbc3593f4aba9b9395b08255b866e54029f48aff6d9b7b1384daf9ab1b21b95f7468678a800fbcb
|
|
@@ -19,7 +19,7 @@ module Masks
|
|
|
19
19
|
answer = handler.call(request.env)
|
|
20
20
|
|
|
21
21
|
return render_rack(answer) unless answer.first == 200
|
|
22
|
-
return
|
|
22
|
+
return ask_for_token if token.nil?
|
|
23
23
|
|
|
24
24
|
yield token
|
|
25
25
|
end
|
|
@@ -50,8 +50,11 @@ module Masks
|
|
|
50
50
|
|
|
51
51
|
def decode(bearer)
|
|
52
52
|
claims_in_token(bearer.access_token)
|
|
53
|
+
rescue JWT::ExpiredSignature
|
|
54
|
+
bearer.invalid_token!("that token has expired")
|
|
53
55
|
rescue JWT::DecodeError => e
|
|
54
|
-
|
|
56
|
+
::Rails.logger.info("masks: an access token was refused: #{e.message}")
|
|
57
|
+
bearer.invalid_token!("that token is not one this server signed")
|
|
55
58
|
end
|
|
56
59
|
|
|
57
60
|
def claims_in_token(secret)
|
|
@@ -96,7 +99,7 @@ module Masks
|
|
|
96
99
|
end
|
|
97
100
|
|
|
98
101
|
def proof_for(secret = nil)
|
|
99
|
-
Proof.read!(request, url: "#{issuer.url}#{request.
|
|
102
|
+
Proof.read!(request, url: "#{issuer.url}#{request.path_info}", access_token: secret)
|
|
100
103
|
end
|
|
101
104
|
|
|
102
105
|
def held_token(secret)
|
|
@@ -118,6 +121,12 @@ module Masks
|
|
|
118
121
|
nil
|
|
119
122
|
end
|
|
120
123
|
|
|
124
|
+
def ask_for_token
|
|
125
|
+
response.headers["WWW-Authenticate"] = %(Bearer realm="#{issuer.url}")
|
|
126
|
+
|
|
127
|
+
head :unauthorized
|
|
128
|
+
end
|
|
129
|
+
|
|
121
130
|
def refuse_token(description)
|
|
122
131
|
render_rack(
|
|
123
132
|
Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
|
|
@@ -143,7 +143,7 @@ module Masks
|
|
|
143
143
|
Current.device = device
|
|
144
144
|
end
|
|
145
145
|
|
|
146
|
-
def sign_in(actor, amr: [], policy: nil, first_factor: {})
|
|
146
|
+
def sign_in(actor, amr: [], policy: nil, first_factor: {}, authenticated_at: nil)
|
|
147
147
|
carried = session.to_hash.slice(REQUESTS, HANDSHAKES, "login", RETURN_TO)
|
|
148
148
|
reset_session
|
|
149
149
|
carried.each { |key, value| session[key] = value }
|
|
@@ -157,7 +157,8 @@ module Masks
|
|
|
157
157
|
ip_address: request.remote_ip,
|
|
158
158
|
amr: amr,
|
|
159
159
|
policy: policy,
|
|
160
|
-
first_factor: first_factor
|
|
160
|
+
first_factor: first_factor,
|
|
161
|
+
authenticated_at: authenticated_at
|
|
161
162
|
)
|
|
162
163
|
|
|
163
164
|
cookies.encrypted[:masks_session] = {
|
|
@@ -301,17 +302,18 @@ module Masks
|
|
|
301
302
|
end
|
|
302
303
|
|
|
303
304
|
def settle!(login)
|
|
304
|
-
hold(login.actor, amr: login.amr, policy: login.policy, first_factor: login.first_factored_by
|
|
305
|
+
hold(login.actor, amr: login.amr, policy: login.policy, first_factor: login.first_factored_by,
|
|
306
|
+
authenticated_at: login.factored_at(:first_factor))
|
|
305
307
|
forget_login
|
|
306
308
|
end
|
|
307
309
|
|
|
308
|
-
def hold(actor, amr: [], policy: nil, first_factor: {})
|
|
310
|
+
def hold(actor, amr: [], policy: nil, first_factor: {}, authenticated_at: nil)
|
|
309
311
|
return if actor.nil?
|
|
310
|
-
return current_session.first_factored!(first_factor) if current_session&.actor_id == actor.id
|
|
312
|
+
return current_session.first_factored!(first_factor, at: authenticated_at) if current_session&.actor_id == actor.id
|
|
311
313
|
|
|
312
314
|
sign_out if current_session
|
|
313
315
|
|
|
314
|
-
sign_in(actor, amr: amr, policy: policy, first_factor: first_factor)
|
|
316
|
+
sign_in(actor, amr: amr, policy: policy, first_factor: first_factor, authenticated_at: authenticated_at)
|
|
315
317
|
end
|
|
316
318
|
|
|
317
319
|
def policy_denied(denial)
|
|
@@ -63,7 +63,7 @@ module Masks
|
|
|
63
63
|
req.bad_request!(:invalid_client, unknown_client(req.client_id)) if client.nil?
|
|
64
64
|
req.invalid_request!("redirect_uri is required") if req.redirect_uri.blank?
|
|
65
65
|
|
|
66
|
-
req.verify_redirect_uri!(client.redirect_uris)
|
|
66
|
+
req.verify_redirect_uri!(client.redirect_uri?(req.redirect_uri) ? [ req.redirect_uri.to_s ] : client.redirect_uris)
|
|
67
67
|
req.verified_redirect_uri = with_issuer(req.verified_redirect_uri)
|
|
68
68
|
res.redirect_uri = req.verified_redirect_uri
|
|
69
69
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Masks
|
|
2
|
+
module Server
|
|
3
|
+
class EnrollmentsController < ApplicationController
|
|
4
|
+
include TokenPresented
|
|
5
|
+
|
|
6
|
+
rate_limit to: 30, within: 1.minute,
|
|
7
|
+
by: -> { [ current_tenant.id, request.remote_ip ].join(":") },
|
|
8
|
+
with: -> {
|
|
9
|
+
render json: {
|
|
10
|
+
"error" => "too_many_requests",
|
|
11
|
+
"error_description" => "too many enrollment tokens asked for from this address"
|
|
12
|
+
}, status: :too_many_requests
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
before_action :no_store!
|
|
16
|
+
|
|
17
|
+
def create
|
|
18
|
+
client = authenticate_app!
|
|
19
|
+
token = EnrollmentToken.issue!(client, scope: params[:scope], name: params[:client_name],
|
|
20
|
+
expires_in: params[:expires_in])
|
|
21
|
+
|
|
22
|
+
Event.record!(Event::CLIENT_ENROLLMENT_ISSUED, client: client, scopes: token.scopes,
|
|
23
|
+
name: token.held("name"), expires_at: token.expires_at.iso8601)
|
|
24
|
+
|
|
25
|
+
render json: {
|
|
26
|
+
"initial_access_token" => token.secret,
|
|
27
|
+
"expires_in" => token.expires_in,
|
|
28
|
+
"scope" => token.scopes
|
|
29
|
+
}, status: :created
|
|
30
|
+
rescue Client::EnrollmentRefused => e
|
|
31
|
+
render json: { "error" => "invalid_request", "error_description" => e.message }, status: :bad_request
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def destroy
|
|
35
|
+
app = authenticate_app!
|
|
36
|
+
machine = app.enrolled.find_by(client_id: params[:machine].to_s)
|
|
37
|
+
|
|
38
|
+
return not_enrolled if machine.nil?
|
|
39
|
+
|
|
40
|
+
revoked = machine.unenroll!
|
|
41
|
+
|
|
42
|
+
Event.record!(Event::CLIENT_UNENROLLED, client: machine, name: machine.name, enrolled_by: app.client_id,
|
|
43
|
+
revoked: revoked)
|
|
44
|
+
|
|
45
|
+
head :no_content
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def authenticate_app!
|
|
51
|
+
client = authenticate_client!
|
|
52
|
+
|
|
53
|
+
client_refused!("a public client proves nothing about itself, so it enrolls and unenrolls nothing") if client.public?
|
|
54
|
+
|
|
55
|
+
client
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def not_enrolled
|
|
59
|
+
render json: {
|
|
60
|
+
"error" => "invalid_request",
|
|
61
|
+
"error_description" => "that client was not enrolled by this app"
|
|
62
|
+
}, status: :not_found
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -13,7 +13,7 @@ module Masks
|
|
|
13
13
|
client = authenticate_client!
|
|
14
14
|
token = presented_token
|
|
15
15
|
|
|
16
|
-
return render json: INACTIVE unless token&.live? && entitled?(client, token)
|
|
16
|
+
return render json: INACTIVE unless token&.live? && !token.client&.archived? && entitled?(client, token)
|
|
17
17
|
|
|
18
18
|
render json: described(token)
|
|
19
19
|
end
|
|
@@ -22,7 +22,7 @@ module Masks
|
|
|
22
22
|
|
|
23
23
|
def entitled?(client, token)
|
|
24
24
|
return true if token.client_id == client.id
|
|
25
|
-
return false
|
|
25
|
+
return false if client.enrolled? || !client.approved?
|
|
26
26
|
|
|
27
27
|
(Array(token.audience) & Array(client.resources)).any?
|
|
28
28
|
end
|
|
@@ -151,7 +151,8 @@ module Masks
|
|
|
151
151
|
return unless login.actor
|
|
152
152
|
|
|
153
153
|
if login.touched?(:first_factor)
|
|
154
|
-
sign_in(login.actor, amr: login.amr, policy: login.policy, first_factor: login.first_factored_by
|
|
154
|
+
sign_in(login.actor, amr: login.amr, policy: login.policy, first_factor: login.first_factored_by,
|
|
155
|
+
authenticated_at: login.factored_at(:first_factor))
|
|
155
156
|
elsif login.signed_in? && login.touched?(:second_factor)
|
|
156
157
|
current_session.update!(amr: Array(current_session.amr) | login.amr)
|
|
157
158
|
end
|
|
@@ -14,6 +14,7 @@ module Masks
|
|
|
14
14
|
|
|
15
15
|
before_action :no_store!
|
|
16
16
|
before_action :require_registration_token, except: :create
|
|
17
|
+
before_action :refuse_enrolled, only: :update
|
|
17
18
|
|
|
18
19
|
def create
|
|
19
20
|
return redeem if bearer.present?
|
|
@@ -26,7 +27,7 @@ module Masks
|
|
|
26
27
|
render json: issued(client), status: :created
|
|
27
28
|
rescue ActiveRecord::RecordInvalid => e
|
|
28
29
|
invalid_metadata(e.record.errors.full_messages.join("; "))
|
|
29
|
-
rescue Client::ScopesUnavailable => e
|
|
30
|
+
rescue Client::ScopesUnavailable, Client::EnrollmentRefused => e
|
|
30
31
|
invalid_metadata(e.message)
|
|
31
32
|
end
|
|
32
33
|
|
|
@@ -47,6 +48,8 @@ module Masks
|
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
def destroy
|
|
51
|
+
return unenroll if @client.enrolled?
|
|
52
|
+
|
|
50
53
|
@client.update!(archived_at: Time.current)
|
|
51
54
|
|
|
52
55
|
Event.record!(Event::CLIENT_ARCHIVED, client: @client, name: @client.name)
|
|
@@ -56,7 +59,27 @@ module Masks
|
|
|
56
59
|
|
|
57
60
|
private
|
|
58
61
|
|
|
62
|
+
def unenroll
|
|
63
|
+
revoked = @client.unenroll!
|
|
64
|
+
|
|
65
|
+
Event.record!(Event::CLIENT_UNENROLLED, client: @client, name: @client.name,
|
|
66
|
+
enrolled_by: @client.enrolled_by&.client_id, revoked: revoked)
|
|
67
|
+
|
|
68
|
+
head :no_content
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def refuse_enrolled
|
|
72
|
+
return unless @client.enrolled?
|
|
73
|
+
|
|
74
|
+
render json: {
|
|
75
|
+
"error" => "access_denied",
|
|
76
|
+
"error_description" => "an enrolled machine is changed only by the app that enrolled it"
|
|
77
|
+
}, status: :forbidden
|
|
78
|
+
end
|
|
79
|
+
|
|
59
80
|
def redeem
|
|
81
|
+
return enrolled(@enrolled) if (@enrolled = EnrollmentToken.redeem!(bearer, body))
|
|
82
|
+
|
|
60
83
|
token = InitialAccessToken.claim(bearer)
|
|
61
84
|
|
|
62
85
|
return unauthorized("that initial access token is not valid or has expired") if token&.client.nil?
|
|
@@ -64,6 +87,13 @@ module Masks
|
|
|
64
87
|
render json: issued(token.issue!), status: :created
|
|
65
88
|
end
|
|
66
89
|
|
|
90
|
+
def enrolled(client)
|
|
91
|
+
Event.record!(Event::CLIENT_ENROLLED, client: client, name: client.name,
|
|
92
|
+
enrolled_by: client.enrolled_by.client_id)
|
|
93
|
+
|
|
94
|
+
render json: issued(client), status: :created
|
|
95
|
+
end
|
|
96
|
+
|
|
67
97
|
def bearer
|
|
68
98
|
request.authorization.to_s[/\ABearer (\S+)\z/, 1]
|
|
69
99
|
end
|
|
@@ -103,6 +133,12 @@ module Masks
|
|
|
103
133
|
}, status: :bad_request
|
|
104
134
|
end
|
|
105
135
|
|
|
136
|
+
def ask_for_token
|
|
137
|
+
response.headers["WWW-Authenticate"] = %(Bearer realm="#{issuer.url}")
|
|
138
|
+
|
|
139
|
+
head :unauthorized
|
|
140
|
+
end
|
|
141
|
+
|
|
106
142
|
def issued(client)
|
|
107
143
|
client.metadata.merge(
|
|
108
144
|
"client_secret" => client.secret,
|
|
@@ -125,7 +161,9 @@ module Masks
|
|
|
125
161
|
end
|
|
126
162
|
|
|
127
163
|
def unauthorized(description)
|
|
128
|
-
|
|
164
|
+
return ask_for_token if bearer.blank?
|
|
165
|
+
|
|
166
|
+
response.headers["WWW-Authenticate"] = %(Bearer realm="#{issuer.url}", error="invalid_token")
|
|
129
167
|
|
|
130
168
|
render json: {
|
|
131
169
|
"error" => "invalid_token",
|
|
@@ -7,7 +7,7 @@ module Masks
|
|
|
7
7
|
def service_provider_config
|
|
8
8
|
scim({
|
|
9
9
|
"schemas" => [ Scim::SERVICE_PROVIDER_CONFIG ],
|
|
10
|
-
"documentationUri" => "#{::Rails.configuration.masks.docs_url}/
|
|
10
|
+
"documentationUri" => "#{::Rails.configuration.masks.docs_url}/guides/sso-saml/#provisioning",
|
|
11
11
|
"patch" => { "supported" => true },
|
|
12
12
|
"bulk" => { "supported" => false, "maxOperations" => 0, "maxPayloadSize" => 0 },
|
|
13
13
|
"filter" => { "supported" => true, "maxResults" => Scim::MAX_RESULTS },
|
|
@@ -7,7 +7,7 @@ module Masks
|
|
|
7
7
|
|
|
8
8
|
argument :preset, ID
|
|
9
9
|
argument :base_url, String, description: "Where the app is served, such as https://grafana.example.com."
|
|
10
|
-
argument :name, String, required: false, description: "What people see.
|
|
10
|
+
argument :name, String, required: false, description: "What people see."
|
|
11
11
|
argument :slug, String, required: false,
|
|
12
12
|
description: "The name some apps put in their redirect URI, such as Gitea's authentication source."
|
|
13
13
|
|
|
@@ -57,6 +57,10 @@ module Masks
|
|
|
57
57
|
field :dynamic, Boolean, null: false
|
|
58
58
|
field :approved_at, GraphQL::Types::ISO8601DateTime
|
|
59
59
|
field :approved_by, ActorType
|
|
60
|
+
field :enrolled_by, ClientType,
|
|
61
|
+
description: "The app that enrolled this client as one of its machines. Archiving that app archives this client."
|
|
62
|
+
field :enrolled, [ ClientType ], null: false,
|
|
63
|
+
description: "The machines this app enrolled that are not archived."
|
|
60
64
|
field :archived_at, GraphQL::Types::ISO8601DateTime
|
|
61
65
|
field :secret_expires_at, GraphQL::Types::ISO8601DateTime
|
|
62
66
|
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
|
|
@@ -75,6 +79,10 @@ module Masks
|
|
|
75
79
|
argument :limit, Integer, required: false
|
|
76
80
|
end
|
|
77
81
|
|
|
82
|
+
def enrolled
|
|
83
|
+
object.enrolled.active.order(:name)
|
|
84
|
+
end
|
|
85
|
+
|
|
78
86
|
def tokens(limit: nil)
|
|
79
87
|
Masks::Server::Token.where(kind: TokenType::GRANTS, client_id: object.id)
|
|
80
88
|
.live
|
|
@@ -169,6 +169,8 @@ module Masks
|
|
|
169
169
|
argument :action, String, required: false
|
|
170
170
|
argument :grave, Boolean, required: false
|
|
171
171
|
argument :organization, ID, required: false
|
|
172
|
+
argument :since, GraphQL::Types::ISO8601DateTime, required: false,
|
|
173
|
+
description: "Only events recorded at or after this time."
|
|
172
174
|
argument :after_id, ID, required: false
|
|
173
175
|
argument :limit, Integer, required: false
|
|
174
176
|
end
|
|
@@ -180,6 +182,8 @@ module Masks
|
|
|
180
182
|
argument :action, String, required: false
|
|
181
183
|
argument :grave, Boolean, required: false
|
|
182
184
|
argument :organization, ID, required: false
|
|
185
|
+
argument :since, GraphQL::Types::ISO8601DateTime, required: false,
|
|
186
|
+
description: "Only events recorded at or after this time."
|
|
183
187
|
end
|
|
184
188
|
|
|
185
189
|
field :event_actions, [ EventActionType ], null: false
|
|
@@ -484,7 +488,7 @@ module Masks
|
|
|
484
488
|
scope.where("name ILIKE :term OR client_id = :exact", term: term, exact: search.strip)
|
|
485
489
|
end
|
|
486
490
|
|
|
487
|
-
def event_scope(actor: nil, client: nil, device: nil, action: nil, grave: false, organization: nil)
|
|
491
|
+
def event_scope(actor: nil, client: nil, device: nil, action: nil, grave: false, organization: nil, since: nil)
|
|
488
492
|
subject = actor.present? ? Actor.find_by(uuid: actor) : nil
|
|
489
493
|
group = organization.present? ? Masks::Server::Organization.find_by(key: organization) : nil
|
|
490
494
|
|
|
@@ -504,6 +508,7 @@ module Masks
|
|
|
504
508
|
scope = scope.where(organization: group) if group
|
|
505
509
|
scope = scope.where(action: action) if action.present?
|
|
506
510
|
scope = scope.where(action: Masks::Server::Event::GRAVE) if grave
|
|
511
|
+
scope = scope.where(created_at: since..) if since
|
|
507
512
|
|
|
508
513
|
scope
|
|
509
514
|
end
|
|
@@ -15,7 +15,7 @@ module Masks
|
|
|
15
15
|
close: '<path d="M6.5 6.5l7 7M13.5 6.5l-7 7"/>',
|
|
16
16
|
check: '<path d="m5.5 10.5 3 3 6-7"/>',
|
|
17
17
|
organization: '<rect x="3.5" y="7" width="13" height="9.5" rx="1.5"/><path d="M7.5 7V4.5h5V7"/><path d="M3.5 11h13"/>',
|
|
18
|
-
|
|
18
|
+
actor: '<circle cx="10" cy="7" r="3.25"/><path d="M4 16.5c.8-3 3.2-4.5 6-4.5s5.2 1.5 6 4.5"/>',
|
|
19
19
|
authenticator: '<rect x="3" y="5.5" width="14" height="9" rx="2"/><path d="M6.5 10h.01M10 10h.01M13.5 10h.01"/>',
|
|
20
20
|
backup_codes: '<path d="M5.5 3.5h9v13h-9z"/><path d="M8 7h4M8 10h4M8 13h4"/>',
|
|
21
21
|
manage: '<path d="M10 2.5 3.5 5v5c0 3.4 2.7 6.2 6.5 7.5 3.8-1.3 6.5-4.1 6.5-7.5V5L10 2.5Z"/>'
|
|
@@ -3,13 +3,41 @@ module Masks
|
|
|
3
3
|
module ScopesHelper
|
|
4
4
|
CONSEQUENTIAL = /(:write|:command|:exec|:delete|:admin)\z|\Aoffline_access\z|\Amasks:manage(:[a-z]+)?\z|\Amasks:delegate:./
|
|
5
5
|
|
|
6
|
+
SEEN = [
|
|
7
|
+
Scopes::OPENID, Scopes::PROFILE, Scopes::EMAIL,
|
|
8
|
+
Scopes::IDENTITIES, Scopes::ORGANIZATION, Scopes::GROUPS
|
|
9
|
+
].freeze
|
|
10
|
+
|
|
6
11
|
def consequential_scope?(scope)
|
|
7
12
|
scope.to_s.match?(CONSEQUENTIAL)
|
|
8
13
|
end
|
|
9
14
|
|
|
15
|
+
def heeded_scope?(scope)
|
|
16
|
+
scope == Scopes::DELEGATE || consequential_scope?(scope)
|
|
17
|
+
end
|
|
18
|
+
|
|
10
19
|
def ranked_scopes(described)
|
|
11
20
|
described.to_a.partition { |scope, _| consequential_scope?(scope) }
|
|
12
21
|
end
|
|
22
|
+
|
|
23
|
+
def plain_scopes(described, areas: [])
|
|
24
|
+
held = { sees: [], heeds: [], also: [] }
|
|
25
|
+
|
|
26
|
+
described.to_a.each do |scope, description|
|
|
27
|
+
next if areas.include?(scope)
|
|
28
|
+
|
|
29
|
+
group =
|
|
30
|
+
if heeded_scope?(scope) then :heeds
|
|
31
|
+
elsif SEEN.include?(scope) then :sees
|
|
32
|
+
else :also
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
held[group] << [ scope, description ]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
held[:sees].sort_by! { |scope, _| SEEN.index(scope) }
|
|
39
|
+
held
|
|
40
|
+
end
|
|
13
41
|
end
|
|
14
42
|
end
|
|
15
43
|
end
|
|
@@ -32,6 +32,7 @@ module Masks
|
|
|
32
32
|
SigningKey.where.not(retired_at: nil).where(retired_at: ...cutoff).delete_all
|
|
33
33
|
Event.where(created_at: ...tenant.event_retention.ago).in_batches(of: BATCH).delete_all
|
|
34
34
|
Members.purge_lapsed!
|
|
35
|
+
Replay.where(expires_at: ...Time.current).in_batches(of: BATCH).delete_all
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
def sweep_tokens(cutoff)
|
|
@@ -190,14 +190,14 @@ module Masks
|
|
|
190
190
|
holds?(Scopes::MANAGE)
|
|
191
191
|
end
|
|
192
192
|
|
|
193
|
-
def
|
|
193
|
+
def card_json
|
|
194
194
|
style = Avatars.held?(self) ? Avatars::PHOTO : Avatars::FALLBACK
|
|
195
195
|
|
|
196
196
|
{
|
|
197
197
|
"name" => display_name,
|
|
198
198
|
"details" => display_details,
|
|
199
|
-
"note" => (I18n.t("application.
|
|
200
|
-
"role" => (I18n.t("application.
|
|
199
|
+
"note" => (I18n.t("application.actor.unconfirmed") if email_unconfirmed?),
|
|
200
|
+
"role" => (I18n.t("application.actor.manager") if manages?),
|
|
201
201
|
"avatar" => Avatars.url(self, style, subject: uuid, size: 128)
|
|
202
202
|
}
|
|
203
203
|
end
|