yiffspace 0.0.25 → 0.1.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/CHANGELOG.md +11 -0
- data/lib/yiffspace/configuration.rb +0 -25
- data/lib/yiffspace/{railtie.rb → engine.rb} +9 -7
- data/lib/yiffspace/extensions/active_record/where_chain.rb +5 -5
- data/lib/yiffspace/extensions/arel/visitors/postgresql/cross_join_lateral.rb +1 -1
- data/lib/yiffspace/extensions/arel/visitors/postgresql/left_join_lateral.rb +1 -1
- data/lib/yiffspace/version.rb +1 -1
- data/lib/yiffspace.rb +5 -6
- metadata +2 -49
- data/app/helpers/yiff_space/auth_helper.rb +0 -6
- data/engines/auth/app/controllers/yiff_space/auth/application_controller.rb +0 -11
- data/engines/auth/app/controllers/yiff_space/auth/root_controller.rb +0 -49
- data/engines/auth/app/controllers/yiff_space/auth/webhook_controller.rb +0 -73
- data/engines/auth/app/views/yiff_space/auth/root/permissions.html.erb +0 -14
- data/engines/auth/config/routes.rb +0 -12
- data/lib/tasks/yiffspace_tasks.rake +0 -41
- data/lib/yiffspace/auth/api_user.rb +0 -43
- data/lib/yiffspace/auth/auth_info/anonymous.rb +0 -60
- data/lib/yiffspace/auth/auth_info.rb +0 -78
- data/lib/yiffspace/auth/client.rb +0 -56
- data/lib/yiffspace/auth/discord_info.rb +0 -65
- data/lib/yiffspace/auth/engine.rb +0 -58
- data/lib/yiffspace/auth/helper.rb +0 -174
- data/lib/yiffspace/auth/permissions.rb +0 -87
- data/lib/yiffspace/auth/set_client_name.rb +0 -15
- data/lib/yiffspace/auth/user_info/anonymous.rb +0 -72
- data/lib/yiffspace/auth/user_info.rb +0 -87
- data/lib/yiffspace/auth.rb +0 -44
- data/lib/yiffspace/core_ext/action_dispatch/set_auth_client/scoped.rb +0 -13
- data/lib/yiffspace/core_ext/action_dispatch/set_auth_client.rb +0 -13
- data/lib/yiffspace/core_ext/logto/named_session_storage.rb +0 -7
- data/lib/yiffspace/extensions/action_dispatch/set_auth_client/scoped.rb +0 -15
- data/lib/yiffspace/extensions/action_dispatch/set_auth_client.rb +0 -13
- data/lib/yiffspace/extensions/logto/named_session_storage.rb +0 -22
- data/lib/yiffspace/logto_management_client.rb +0 -136
- data/lib/yiffspace/serializers/anonymous_auth_info_serializer.rb +0 -23
- data/lib/yiffspace/serializers/anonymous_user_info_serializer.rb +0 -23
- data/lib/yiffspace/serializers/auth_info_serializer.rb +0 -23
- data/lib/yiffspace/serializers/discord_info_serializer.rb +0 -23
- data/lib/yiffspace/serializers/permissions_serializer.rb +0 -23
- data/lib/yiffspace/serializers/user_info_serializer.rb +0 -23
- data/lib/yiffspace/utils/user_deduper.rb +0 -66
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module YiffSpace
|
|
4
|
-
module Auth
|
|
5
|
-
class AuthInfo
|
|
6
|
-
attr_reader(:id, :token, :roles, :permissions, :client_id)
|
|
7
|
-
|
|
8
|
-
# @param id String
|
|
9
|
-
# @param roles Array(String)
|
|
10
|
-
# @param permissions Array(String)
|
|
11
|
-
# @param token LogtoClient::AccessTokenClaims
|
|
12
|
-
# @param client_id String
|
|
13
|
-
def initialize(id:, roles:, permissions:, token:, client_id:)
|
|
14
|
-
raise(ArgumentError, "no id provided") if id.blank?
|
|
15
|
-
raise(ArgumentError, "no token provided") if token.blank?
|
|
16
|
-
raise(ArgumentError, "no client id provided") if client_id.blank?
|
|
17
|
-
|
|
18
|
-
@id = id
|
|
19
|
-
@token = token
|
|
20
|
-
@roles = Array(roles)
|
|
21
|
-
@permissions = Permissions.new(permissions, separator: YiffSpace::Auth.get_by_id(client_id).permissions_separator)
|
|
22
|
-
@client_id = client_id
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def anonymous?
|
|
26
|
-
false
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# this feels wrong, but it hopefully shouldn't break anything
|
|
30
|
-
def present?
|
|
31
|
-
true
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def blank?
|
|
35
|
-
false
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def has_permission?(name)
|
|
39
|
-
permissions.include?(name.to_s)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def serializable_hash(*)
|
|
43
|
-
{
|
|
44
|
-
"id" => id,
|
|
45
|
-
"token" => token.as_json,
|
|
46
|
-
"roles" => roles,
|
|
47
|
-
"permissions" => permissions.values,
|
|
48
|
-
"client_id" => client_id,
|
|
49
|
-
}
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def to_session
|
|
53
|
-
serializable_hash
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def self.from_json(data)
|
|
57
|
-
raise(ArgumentError, "invalid data") if data.blank?
|
|
58
|
-
|
|
59
|
-
data = JSON.parse(data) if data.is_a?(String)
|
|
60
|
-
data = ::YiffSpace::Utils::OpenHash.from(data)
|
|
61
|
-
|
|
62
|
-
new(
|
|
63
|
-
id: data.id,
|
|
64
|
-
token: data.token,
|
|
65
|
-
roles: data.roles,
|
|
66
|
-
permissions: data.permissions,
|
|
67
|
-
client_id: data.client_id,
|
|
68
|
-
)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def self.from_session(data)
|
|
72
|
-
return nil if data.blank?
|
|
73
|
-
|
|
74
|
-
from_json(data)
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative("../core_ext/logto/named_session_storage")
|
|
4
|
-
require("httparty")
|
|
5
|
-
|
|
6
|
-
module YiffSpace
|
|
7
|
-
module Auth
|
|
8
|
-
class Client
|
|
9
|
-
attr_accessor(:client_id, :client_secret, :scopes, :resource,
|
|
10
|
-
:redirect_uri, :server_url, :auth_session_key, :user_session_key,
|
|
11
|
-
:update_discord_images, :permissions_separator, :logto_webhook_secret)
|
|
12
|
-
|
|
13
|
-
attr_reader(:name)
|
|
14
|
-
|
|
15
|
-
def initialize(name)
|
|
16
|
-
@name = name.to_sym
|
|
17
|
-
@scopes = %i[openid offline_access profile roles identities custom_data]
|
|
18
|
-
@redirect_uri = "http://127.0.0.1:3000/auth/cb"
|
|
19
|
-
@server_url = "https://auth.yiff.space"
|
|
20
|
-
@auth_session_key = :"yiffspace_auth_#{name}"
|
|
21
|
-
@user_session_key = :"yiffspace_user_#{name}"
|
|
22
|
-
@update_discord_images = true
|
|
23
|
-
@permissions_separator = ":"
|
|
24
|
-
@logto_webhook_secret = nil
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def logto(controller)
|
|
28
|
-
LogtoClient.new(
|
|
29
|
-
config: LogtoClient::Config.new(
|
|
30
|
-
endpoint: server_url,
|
|
31
|
-
app_id: client_id,
|
|
32
|
-
app_secret: client_secret,
|
|
33
|
-
scopes: scopes,
|
|
34
|
-
resources: [resource].compact,
|
|
35
|
-
),
|
|
36
|
-
# Allow the client to redirect to other hosts (i.e. your Logto tenant)
|
|
37
|
-
navigate: ->(uri) { controller.redirect_to(uri, allow_other_host: true) },
|
|
38
|
-
# Controller has access to the session object
|
|
39
|
-
storage: LogtoClient::NamedSessionStorage.new("yiffspace", controller.session, app_id: @name),
|
|
40
|
-
)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def logto_management
|
|
44
|
-
@logto_management ||= LogtoManagementClient.new(self)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def fetch_discord_user(id)
|
|
48
|
-
response = HTTParty.get("https://discord.com/api/v10/users/#{id}", { headers: { "Authorization" => "Bot #{YiffSpace.config.discord_bot_token}" } })
|
|
49
|
-
return nil if response.code == 404
|
|
50
|
-
raise("failed to fetch discord user: #{response.code} #{response.message}\n#{response.parsed_response.inspect}") if response.code != 200
|
|
51
|
-
|
|
52
|
-
DiscordInfo.new(response.parsed_response)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module YiffSpace
|
|
4
|
-
module Auth
|
|
5
|
-
class DiscordInfo
|
|
6
|
-
# locale, mfa_enabled, and premium_type are not present for legacy users backfilled from api data,
|
|
7
|
-
# we don't need that data either way
|
|
8
|
-
ATTRIBUTES = %i[id flags avatar banner username global_name public_flags discriminator].freeze
|
|
9
|
-
attr_reader(:data, *ATTRIBUTES)
|
|
10
|
-
|
|
11
|
-
def initialize(options = {}, **kwargs)
|
|
12
|
-
@data = options.merge(kwargs).with_indifferent_access
|
|
13
|
-
ATTRIBUTES.each do |attr|
|
|
14
|
-
instance_variable_set("@#{attr}", @data[attr])
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
return unless YiffSpace.config.auth.update_discord_images
|
|
18
|
-
|
|
19
|
-
last_avatar = Rails.cache.fetch("yiffspace:auth:discord_avatar_update:#{id}")
|
|
20
|
-
last_banner = Rails.cache.fetch("yiffspace:auth:discord_banner_update:#{id}")
|
|
21
|
-
|
|
22
|
-
if last_avatar != avatar
|
|
23
|
-
Rails.cache.write("yiffspace:auth:discord_avatar_update:#{id}", avatar, expires_in: 30.days)
|
|
24
|
-
update_avatar
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
return unless last_banner != banner
|
|
28
|
-
|
|
29
|
-
Rails.cache.write("yiffspace:auth:discord_banner_update:#{id}", banner, expires_in: 30.days)
|
|
30
|
-
update_banner
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def update_avatar
|
|
34
|
-
Images::Avatar.get_for(id, :discord).update(avatar)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def update_banner
|
|
38
|
-
Images::Banner.get_for(id, :discord).update(banner)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def serializable_hash(*)
|
|
42
|
-
ATTRIBUTES.to_h { |key| [key.to_s, instance_variable_get("@#{key}")] }
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def to_session
|
|
46
|
-
serializable_hash
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def self.from_json(data)
|
|
50
|
-
raise(ArgumentError, "invalid data") if data.blank?
|
|
51
|
-
|
|
52
|
-
data = JSON.parse(data) if data.is_a?(String)
|
|
53
|
-
data = ::YiffSpace::Utils::OpenHash.from(data)
|
|
54
|
-
|
|
55
|
-
new(data)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def self.from_session(data)
|
|
59
|
-
return nil if data.blank?
|
|
60
|
-
|
|
61
|
-
from_json(data)
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require("rails")
|
|
4
|
-
|
|
5
|
-
module YiffSpace
|
|
6
|
-
module Auth
|
|
7
|
-
class Engine < ::Rails::Engine
|
|
8
|
-
isolate_namespace(YiffSpace::Auth)
|
|
9
|
-
config.root = File.expand_path("../../../engines/auth", __dir__)
|
|
10
|
-
shared_root = File.expand_path("../../../app", __dir__)
|
|
11
|
-
shared_controllers = "#{shared_root}/controllers"
|
|
12
|
-
shared_helpers = "#{shared_root}/helpers"
|
|
13
|
-
shared_models = "#{shared_root}/models"
|
|
14
|
-
shared_views = "#{shared_root}/views"
|
|
15
|
-
shared_assets = "#{shared_root}/assets"
|
|
16
|
-
|
|
17
|
-
paths["app/controllers"] << shared_controllers
|
|
18
|
-
paths["app/helpers"] << shared_helpers
|
|
19
|
-
paths["app/models"] << shared_models
|
|
20
|
-
paths["app/views"] << shared_views
|
|
21
|
-
paths["app/assets"] << shared_assets
|
|
22
|
-
|
|
23
|
-
config.autoload_paths += [shared_controllers, shared_helpers, shared_models]
|
|
24
|
-
config.eager_load_paths += [shared_controllers, shared_helpers, shared_models]
|
|
25
|
-
|
|
26
|
-
initializer("yiffspace.auth.asset_paths") do |app|
|
|
27
|
-
if app.config.respond_to?(:assets)
|
|
28
|
-
app.config.assets.paths << "#{shared_assets}/config"
|
|
29
|
-
app.config.assets.precompile += %w[yiffspace/application.css]
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
class << self
|
|
34
|
-
def for(name)
|
|
35
|
-
@instances ||= {}
|
|
36
|
-
@instances[name.to_sym] ||= begin
|
|
37
|
-
subclass = Class.new(self)
|
|
38
|
-
subclass.engine_name("yiffspace_auth_#{name}")
|
|
39
|
-
# Inherit isolation settings that aren't copied from the parent class
|
|
40
|
-
subclass.instance_variable_set(:@isolated, true)
|
|
41
|
-
subclass.routes.default_scope = { module: "yiff_space/auth" }
|
|
42
|
-
subclass.routes.draw do
|
|
43
|
-
constraints(SetClientName.new(name)) do
|
|
44
|
-
post(:webhook, controller: :webhook, action: :create)
|
|
45
|
-
get(:cb, controller: :root)
|
|
46
|
-
get(:logout, controller: :root)
|
|
47
|
-
get(:permissions, controller: :root)
|
|
48
|
-
get(:debug, controller: :root) if ::YiffSpace::Auth.enable_debug_action?
|
|
49
|
-
root(action: :show, controller: :root, as: :auth)
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
subclass
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
@@ -1,174 +0,0 @@
|
|
|
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
|
|
@@ -1,87 +0,0 @@
|
|
|
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
|
|
@@ -1,15 +0,0 @@
|
|
|
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
|
|
@@ -1,72 +0,0 @@
|
|
|
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
|
|
@@ -1,87 +0,0 @@
|
|
|
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
|