yiffspace-auth 0.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 +7 -0
- data/CHANGELOG.md +4 -0
- data/LICENSE +20 -0
- data/README.md +41 -0
- data/Rakefile +10 -0
- data/app/controllers/yiff_space/auth/application_controller.rb +11 -0
- data/app/controllers/yiff_space/auth/root_controller.rb +49 -0
- data/app/controllers/yiff_space/auth/webhook_controller.rb +73 -0
- data/app/helpers/yiff_space/auth_helper.rb +6 -0
- data/app/views/yiff_space/auth/root/permissions.html.erb +14 -0
- data/config/routes.rb +12 -0
- data/lib/tasks/yiffspace_auth_tasks.rake +41 -0
- data/lib/yiffspace/auth/api_user.rb +43 -0
- data/lib/yiffspace/auth/auth_info/anonymous.rb +60 -0
- data/lib/yiffspace/auth/auth_info.rb +78 -0
- data/lib/yiffspace/auth/client.rb +56 -0
- data/lib/yiffspace/auth/discord_info.rb +65 -0
- data/lib/yiffspace/auth/engine.rb +49 -0
- data/lib/yiffspace/auth/helper.rb +174 -0
- data/lib/yiffspace/auth/permissions.rb +87 -0
- data/lib/yiffspace/auth/set_client_name.rb +15 -0
- data/lib/yiffspace/auth/user_info/anonymous.rb +72 -0
- data/lib/yiffspace/auth/user_info.rb +87 -0
- data/lib/yiffspace/auth/version.rb +7 -0
- data/lib/yiffspace/auth.rb +81 -0
- data/lib/yiffspace/core_ext/action_dispatch/set_auth_client/scoped.rb +13 -0
- data/lib/yiffspace/core_ext/action_dispatch/set_auth_client.rb +13 -0
- data/lib/yiffspace/core_ext/logto/named_session_storage.rb +7 -0
- data/lib/yiffspace/extensions/action_dispatch/set_auth_client/scoped.rb +15 -0
- data/lib/yiffspace/extensions/action_dispatch/set_auth_client.rb +13 -0
- data/lib/yiffspace/extensions/logto/named_session_storage.rb +22 -0
- data/lib/yiffspace/logto_management_client.rb +136 -0
- data/lib/yiffspace/serializers/anonymous_auth_info_serializer.rb +23 -0
- data/lib/yiffspace/serializers/anonymous_user_info_serializer.rb +23 -0
- data/lib/yiffspace/serializers/auth_info_serializer.rb +23 -0
- data/lib/yiffspace/serializers/discord_info_serializer.rb +23 -0
- data/lib/yiffspace/serializers/permissions_serializer.rb +23 -0
- data/lib/yiffspace/serializers/user_info_serializer.rb +23 -0
- data/lib/yiffspace/utils/user_deduper.rb +66 -0
- metadata +153 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require("active_support/concern")
|
|
4
|
+
|
|
5
|
+
module YiffSpace
|
|
6
|
+
module Auth
|
|
7
|
+
module Helper
|
|
8
|
+
extend(ActiveSupport::Concern)
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
def set_client_name(name) # rubocop:disable Naming/AccessorMethodName
|
|
12
|
+
before_action do |controller|
|
|
13
|
+
controller.request.env["yiffspace.auth.client_name"] = name
|
|
14
|
+
if controller.respond_to?(:yiffspace_client_name=)
|
|
15
|
+
controller.yiffspace_client_name = name
|
|
16
|
+
elsif controller.respond_to?(:client_name=)
|
|
17
|
+
controller.client_name = name
|
|
18
|
+
elsif controller.respond_to?(:helpers)
|
|
19
|
+
if controller.helpers.respond_to?(:yiffspace_client_name=)
|
|
20
|
+
controller.helpers.yiffspace_client_name = name
|
|
21
|
+
elsif controller.helpers.respond_to?(:client_name=)
|
|
22
|
+
controller.helpers.client_name = name
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def auth_raw
|
|
30
|
+
session[auth_client_config.auth_session_key]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def auth
|
|
34
|
+
return AuthInfo::Anonymous.instance if auth_raw.blank?
|
|
35
|
+
|
|
36
|
+
AuthInfo.from_session(auth_raw)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def auth?
|
|
40
|
+
auth_raw.present? && !auth.anonymous?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def auth=(value)
|
|
44
|
+
value = nil if value.is_a?(AuthInfo::Anonymous)
|
|
45
|
+
session[auth_client_config.auth_session_key] = value&.to_session
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def reset_auth!
|
|
49
|
+
session.delete(auth_client_config.auth_session_key)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def user_raw
|
|
53
|
+
session[auth_client_config.user_session_key]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def user
|
|
57
|
+
return UserInfo::Anonymous.instance if user_raw.blank?
|
|
58
|
+
|
|
59
|
+
UserInfo.from_session(user_raw)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def user?
|
|
63
|
+
user_raw.present? && !user.anonymous?
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def user=(value)
|
|
67
|
+
value = nil if value.is_a?(UserInfo::Anonymous)
|
|
68
|
+
session[auth_client_config.user_session_key] = value&.to_session
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def reset_user!
|
|
72
|
+
session.delete(auth_client_config.user_session_key)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def full_reset!
|
|
76
|
+
reset_auth!
|
|
77
|
+
reset_user!
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def require_auth(path)
|
|
81
|
+
redirect_to(path) unless logged_in?
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def logged_in?
|
|
85
|
+
auth? && user?
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def has_permission?(name)
|
|
89
|
+
return false unless logged_in?
|
|
90
|
+
|
|
91
|
+
auth.permissions.has?(name)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
DIRTY_FLAG_KEY = "yiffspace:auth:dirty:%s"
|
|
95
|
+
|
|
96
|
+
# Checks the dirty flag written by the Logto webhook handler. If set, re-fetches
|
|
97
|
+
# the user's current roles and permissions from the Logto Management API and
|
|
98
|
+
# rewrites the session — without waiting for the access token to expire.
|
|
99
|
+
# Call this as a before_action in any controller that needs instant revocation.
|
|
100
|
+
def sync_auth_if_dirty!
|
|
101
|
+
return unless auth?
|
|
102
|
+
|
|
103
|
+
flag_key = format(DIRTY_FLAG_KEY, auth.id)
|
|
104
|
+
return unless Rails.cache.exist?(flag_key)
|
|
105
|
+
|
|
106
|
+
Rails.cache.delete(flag_key)
|
|
107
|
+
|
|
108
|
+
management = auth_client_config.logto_management
|
|
109
|
+
api_user = management.get_user_by_id(auth.id)
|
|
110
|
+
|
|
111
|
+
if api_user.nil? || api_user.data["isSuspended"]
|
|
112
|
+
full_reset!
|
|
113
|
+
return
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
roles = management.get_user_roles(auth.id)
|
|
117
|
+
permissions = roles.flat_map { |role| management.get_role_scopes(role["id"]) }
|
|
118
|
+
.pluck("name")
|
|
119
|
+
.uniq
|
|
120
|
+
|
|
121
|
+
self.auth = AuthInfo.new(
|
|
122
|
+
id: auth.id,
|
|
123
|
+
token: auth.token,
|
|
124
|
+
roles: roles.pluck("name"),
|
|
125
|
+
permissions: permissions,
|
|
126
|
+
client_id: auth.client_id,
|
|
127
|
+
)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def url_helpers
|
|
131
|
+
YiffSpace::Auth::Engine.for(client_name).routes.url_helpers
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Returns the Auth::Client for the current request. In auth engine controllers this is
|
|
135
|
+
# resolved from the routing default set by Engine.for; in host app controllers it falls
|
|
136
|
+
# back to the default registered client. Override in your controller to choose a specific
|
|
137
|
+
# client when multiple are registered.
|
|
138
|
+
def auth_client_config
|
|
139
|
+
client_name = self.client_name
|
|
140
|
+
client_name.present? ? YiffSpace::Auth[client_name.to_sym] : YiffSpace::Auth.default
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def client_name
|
|
144
|
+
respond_to?(:request, true) && request.env[CLIENT_NAME_ENV]
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def client_name=(value)
|
|
148
|
+
request.env[CLIENT_NAME_ENV] = value.to_sym
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
module Scoped
|
|
152
|
+
extend(ActiveSupport::Concern)
|
|
153
|
+
include(Helper)
|
|
154
|
+
|
|
155
|
+
included do
|
|
156
|
+
private(*Helper.instance_methods(false))
|
|
157
|
+
private_class_method(*Helper::ClassMethods.instance_methods(false))
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
Helper.instance_methods(false).each do |name|
|
|
161
|
+
define_method("yiffspace_#{name}") { |*args, **kwargs, &block| send(name, *args, **kwargs, &block) }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
module ClassMethods
|
|
165
|
+
include(Helper::ClassMethods)
|
|
166
|
+
|
|
167
|
+
Helper::ClassMethods.instance_methods(false).each do |name|
|
|
168
|
+
define_method("yiffspace_#{name}") { |*args, **kwargs, &block| send(name, *args, **kwargs, &block) }
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module YiffSpace
|
|
4
|
+
module Auth
|
|
5
|
+
class Permissions
|
|
6
|
+
attr_accessor(:values)
|
|
7
|
+
|
|
8
|
+
delegate(:each, to: :values)
|
|
9
|
+
|
|
10
|
+
include(Enumerable)
|
|
11
|
+
|
|
12
|
+
def initialize(perms, separator: ":")
|
|
13
|
+
@values = perms
|
|
14
|
+
@tree = {}
|
|
15
|
+
@separator = separator
|
|
16
|
+
|
|
17
|
+
perms.each do |perm|
|
|
18
|
+
current = @tree
|
|
19
|
+
parts = perm.split(separator)
|
|
20
|
+
|
|
21
|
+
parts.each do |part|
|
|
22
|
+
current[part] ||= {}
|
|
23
|
+
current = current[part]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# mark leaf
|
|
27
|
+
current[:__leaf__] = true
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def has?(perm)
|
|
32
|
+
current = @tree
|
|
33
|
+
parts = perm.split(@separator)
|
|
34
|
+
|
|
35
|
+
parts.each do |part|
|
|
36
|
+
return false unless current[part]
|
|
37
|
+
|
|
38
|
+
current = current[part]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
current[:__leaf__] == true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
alias include? has?
|
|
45
|
+
|
|
46
|
+
def method_missing(name, *_args)
|
|
47
|
+
str = name.to_s
|
|
48
|
+
|
|
49
|
+
if str.end_with?("?")
|
|
50
|
+
key = str[0..-2]
|
|
51
|
+
return @tree[key]&.dig(:__leaf__) == true
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if @tree.key?(str)
|
|
55
|
+
self.class.new_from_subtree(@tree[str], @separator)
|
|
56
|
+
else
|
|
57
|
+
# return empty node instead of raising
|
|
58
|
+
self.class.new([], separator: @separator)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def respond_to_missing?(_name, _include_private = false)
|
|
63
|
+
true
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.new_from_subtree(tree, separator)
|
|
67
|
+
obj = allocate
|
|
68
|
+
obj.instance_variable_set(:@tree, tree)
|
|
69
|
+
obj.instance_variable_set(:@values, leaves_from_tree(tree))
|
|
70
|
+
obj.instance_variable_set(:@separator, separator)
|
|
71
|
+
obj
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.leaves_from_tree(node, prefix = "")
|
|
75
|
+
result = []
|
|
76
|
+
node.each do |key, subtree|
|
|
77
|
+
next if key == :__leaf__
|
|
78
|
+
|
|
79
|
+
path = prefix.empty? ? key : "#{prefix}.#{key}"
|
|
80
|
+
result << path if subtree[:__leaf__]
|
|
81
|
+
result.concat(leaves_from_tree(subtree, path))
|
|
82
|
+
end
|
|
83
|
+
result
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module YiffSpace
|
|
4
|
+
module Auth
|
|
5
|
+
class SetClientName < Utils::SetEnvConstraint
|
|
6
|
+
def initialize(value)
|
|
7
|
+
super(CLIENT_NAME_ENV, value.to_sym)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.default
|
|
11
|
+
new(DEFAULT_CLIENT_NAME)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require("singleton")
|
|
4
|
+
|
|
5
|
+
module YiffSpace
|
|
6
|
+
module Auth
|
|
7
|
+
class UserInfo
|
|
8
|
+
class Anonymous
|
|
9
|
+
include(::Singleton)
|
|
10
|
+
|
|
11
|
+
%i[id user discord avatar].each do |attr|
|
|
12
|
+
define_method(attr) { |*, **| raise(NotImplementedError, "#{attr} is not present on anonymous user") }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def username
|
|
16
|
+
"anonymous"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def display_name
|
|
20
|
+
"Anonymous"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def avatar
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def avatar_url
|
|
28
|
+
nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def banner
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def banner_url
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def anonymous?
|
|
40
|
+
true
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# this feels wrong, but it hopefully shouldn't break anything
|
|
44
|
+
def present?
|
|
45
|
+
false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def blank?
|
|
49
|
+
true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def serializable_hash(*)
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def to_session
|
|
57
|
+
serializable_hash
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.from_json(*)
|
|
61
|
+
Anonymous.instance
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.from_session(data)
|
|
65
|
+
return nil if data.blank?
|
|
66
|
+
|
|
67
|
+
from_json(data)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module YiffSpace
|
|
4
|
+
module Auth
|
|
5
|
+
class UserInfo
|
|
6
|
+
attr_reader(:id, :user, :discord)
|
|
7
|
+
|
|
8
|
+
# @param id String
|
|
9
|
+
# @param user LogtoCore::UserInfoResponse
|
|
10
|
+
# @param discord Hash
|
|
11
|
+
def initialize(id:, user:, discord:)
|
|
12
|
+
raise(ArgumentError, "no id provided") if id.blank?
|
|
13
|
+
raise(ArgumentError, "no user provided") if user.blank?
|
|
14
|
+
|
|
15
|
+
@id = id
|
|
16
|
+
@user = user
|
|
17
|
+
@discord = DiscordInfo.from_json(discord)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def anonymous?
|
|
21
|
+
false
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# this feels wrong, but it hopefully shouldn't break anything
|
|
25
|
+
def present?
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def blank?
|
|
30
|
+
false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def avatar(type = nil)
|
|
34
|
+
type.present? ? Images::Avatar.get_for(id, type) : Images::Avatar.default_for(id)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def avatar_url(type = nil)
|
|
38
|
+
avatar(type).url
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def banner(type = nil)
|
|
42
|
+
type.present? ? Images::Banner.get_for(id, type) : Images::Banner.default_for(id)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def banner_url(type = nil)
|
|
46
|
+
banner(type).url
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
delegate(:username, to: :discord)
|
|
50
|
+
|
|
51
|
+
def display_name
|
|
52
|
+
discord.global_name
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def serializable_hash(_options = {})
|
|
56
|
+
{
|
|
57
|
+
"id" => id,
|
|
58
|
+
"discord" => discord.serializable_hash,
|
|
59
|
+
"user" => user.as_json,
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def to_session
|
|
64
|
+
serializable_hash.without("discord").merge(discord: discord.to_session)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.from_json(data)
|
|
68
|
+
raise(ArgumentError, "invalid data") if data.blank?
|
|
69
|
+
|
|
70
|
+
data = JSON.parse(data) if data.is_a?(String)
|
|
71
|
+
data = ::YiffSpace::Utils::OpenHash.from(data)
|
|
72
|
+
|
|
73
|
+
new(
|
|
74
|
+
id: data.id,
|
|
75
|
+
discord: data.discord,
|
|
76
|
+
user: data.user,
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def self.from_session(data)
|
|
81
|
+
return nil if data.blank?
|
|
82
|
+
|
|
83
|
+
from_json(data)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require("yiffspace")
|
|
4
|
+
require("zeitwerk")
|
|
5
|
+
|
|
6
|
+
loader = Zeitwerk::Loader.for_gem_extension(YiffSpace)
|
|
7
|
+
loader.ignore("#{__dir__}/core_ext")
|
|
8
|
+
loader.ignore("#{__dir__}/auth/engine.rb")
|
|
9
|
+
loader.setup
|
|
10
|
+
|
|
11
|
+
# Require the auth engine eagerly so it registers with Rails before the host app's
|
|
12
|
+
# active_support.initialize_per_engine_zeitwerk_loaders initializer runs. Without this,
|
|
13
|
+
# the engine is only loaded during route drawing (after Zeitwerk setup) and its
|
|
14
|
+
# app/controllers path is never added to the app's autoload roots.
|
|
15
|
+
require_relative("auth/engine") if defined?(Rails)
|
|
16
|
+
|
|
17
|
+
module YiffSpace
|
|
18
|
+
module Auth
|
|
19
|
+
CLIENT_NAME_ENV = "yiffspace.auth.client_name"
|
|
20
|
+
DEFAULT_CLIENT_NAME = :default
|
|
21
|
+
|
|
22
|
+
@clients = {}
|
|
23
|
+
@enable_debug_action = false
|
|
24
|
+
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
def register(name, &block)
|
|
28
|
+
client = Client.new(name)
|
|
29
|
+
block&.call(client)
|
|
30
|
+
@clients[name.to_sym] = client
|
|
31
|
+
client
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def [](name)
|
|
35
|
+
@clients[name.to_sym] || raise(KeyError, "unknown auth client: #{name.inspect}")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def default
|
|
39
|
+
@clients[DEFAULT_CLIENT_NAME] || raise("no default client configured")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def get_by_id(id)
|
|
43
|
+
@clients.values.find { |c| c.client_id == id } || raise(ArgumentError, "unable to find client with id: #{id}")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def enable_debug_action?
|
|
47
|
+
@enable_debug_action
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def enable_debug_action!
|
|
51
|
+
@enable_debug_action = true
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def disable_debug_action!
|
|
55
|
+
@enable_debug_action = false
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class Configuration
|
|
60
|
+
# Logto Management API credentials (shared across all auth clients).
|
|
61
|
+
attr_accessor(:logto_api_client_id, :logto_api_client_secret, :logto_api_resource)
|
|
62
|
+
|
|
63
|
+
# Discord bot token used to look up Discord users (shared across all auth clients).
|
|
64
|
+
attr_accessor(:discord_bot_token)
|
|
65
|
+
|
|
66
|
+
def auth(&block)
|
|
67
|
+
client = YiffSpace::Auth.register(Auth::DEFAULT_CLIENT_NAME) unless YiffSpace::Auth.instance_variable_get(:@clients).key?(Auth::DEFAULT_CLIENT_NAME)
|
|
68
|
+
client ||= YiffSpace::Auth[Auth::DEFAULT_CLIENT_NAME]
|
|
69
|
+
block&.call(client)
|
|
70
|
+
client
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def add_auth(name, &)
|
|
74
|
+
YiffSpace::Auth.register(name, &)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def add_default_auth(&)
|
|
78
|
+
add_auth(Auth::DEFAULT_CLIENT_NAME, &)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative("../../../extensions/action_dispatch/set_auth_client/scoped")
|
|
4
|
+
|
|
5
|
+
module ActionDispatch
|
|
6
|
+
module Routing
|
|
7
|
+
class Mapper
|
|
8
|
+
module Scoping
|
|
9
|
+
include(YiffSpace::Extensions::ActionDispatch::SetAuthClient::Scoped)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative("../../extensions/action_dispatch/set_auth_client")
|
|
4
|
+
|
|
5
|
+
module ActionDispatch
|
|
6
|
+
module Routing
|
|
7
|
+
class Mapper
|
|
8
|
+
module Scoping
|
|
9
|
+
include(YiffSpace::Extensions::ActionDispatch::SetAuthClient)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module YiffSpace
|
|
4
|
+
module Extensions
|
|
5
|
+
module ActionDispatch
|
|
6
|
+
module SetAuthClient
|
|
7
|
+
module Scoped
|
|
8
|
+
def yiffspace_set_auth_client(name, &)
|
|
9
|
+
constraints(Auth::SetClientName.new(name), &)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require("logto/client")
|
|
4
|
+
|
|
5
|
+
module YiffSpace
|
|
6
|
+
module Extensions
|
|
7
|
+
module Logto
|
|
8
|
+
class NamedSessionStorage < LogtoClient::SessionStorage
|
|
9
|
+
def initialize(name, session, app_id: nil)
|
|
10
|
+
super(session, app_id: app_id)
|
|
11
|
+
@name = name
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
protected
|
|
15
|
+
|
|
16
|
+
def get_session_key(key)
|
|
17
|
+
"#{@name}_#{@app_id || 'default'}_#{key}"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|