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,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require("active_support/core_ext/integer/time")
|
|
4
|
+
require("httparty")
|
|
5
|
+
|
|
6
|
+
module YiffSpace
|
|
7
|
+
class LogtoManagementClient
|
|
8
|
+
attr_reader(:auth)
|
|
9
|
+
|
|
10
|
+
def initialize(auth)
|
|
11
|
+
@auth = auth
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get_token # rubocop:disable Naming/AccessorMethodName
|
|
15
|
+
if @_cached_token
|
|
16
|
+
return @_cached_token if @_cache_expire_time > Time.current
|
|
17
|
+
|
|
18
|
+
@_cached_token = nil
|
|
19
|
+
@_cache_expire_time = nil
|
|
20
|
+
end
|
|
21
|
+
response = HTTParty.post("#{auth.server_url}/oidc/token", {
|
|
22
|
+
body: {
|
|
23
|
+
grant_type: "client_credentials",
|
|
24
|
+
client_id: YiffSpace.config.logto_api_client_id,
|
|
25
|
+
client_secret: YiffSpace.config.logto_api_client_secret,
|
|
26
|
+
resource: YiffSpace.config.logto_api_resource,
|
|
27
|
+
scope: "all",
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
raise("failed to get access token: #{response.to_json}") unless response.key?("access_token")
|
|
31
|
+
|
|
32
|
+
@_cached_token = response["access_token"]
|
|
33
|
+
@_cache_expire_time = response["expires_in"].to_i.seconds.from_now
|
|
34
|
+
response["access_token"]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def get_user(id)
|
|
38
|
+
response = HTTParty.get("#{auth.server_url}/api/users?discordId=#{id}", { headers: { "Authorization" => "Bearer #{get_token}" } })
|
|
39
|
+
return nil if response.code == 404 || (response.parsed_response.is_a?(Array) && response.parsed_response.empty?)
|
|
40
|
+
raise("failed to get user: #{response.code} #{response.message}\n#{response.parsed_response.inspect}") if response.code != 200 || !response.parsed_response.is_a?(Array)
|
|
41
|
+
|
|
42
|
+
Utils::TraceLogger.warn("LogtoManagementClient", "query discordId=#{id} returned more than one user:\n#{response.parsed_response.inspect}") if response.parsed_response.length > 1
|
|
43
|
+
|
|
44
|
+
Auth::ApiUser.new(response.parsed_response.first)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def create_user(id)
|
|
48
|
+
details = auth.fetch_discord_user(id)&.data
|
|
49
|
+
raise("invalid discord user: #{id}") if details.blank?
|
|
50
|
+
|
|
51
|
+
response = HTTParty.post("#{auth.server_url}/api/users", {
|
|
52
|
+
headers: { "Authorization" => "Bearer #{get_token}", "Content-Type" => "application/json" },
|
|
53
|
+
body: { avatar: "https://cdn.discordapp.com/avatars/#{details['id']}/#{details['avatar']}", name: details["username"] }.to_json,
|
|
54
|
+
})
|
|
55
|
+
raise("failed to create user: #{response.code} #{response.message}\n#{response.parsed_response.inspect}") if response.code != 200
|
|
56
|
+
|
|
57
|
+
response2 = HTTParty.put("#{auth.server_url}/api/users/#{response.parsed_response['id']}/identities/discord", {
|
|
58
|
+
headers: { "Authorization" => "Bearer #{get_token}", "Content-Type" => "application/json" },
|
|
59
|
+
body: {
|
|
60
|
+
userId: details["id"],
|
|
61
|
+
details: {
|
|
62
|
+
id: details["id"],
|
|
63
|
+
name: details["username"],
|
|
64
|
+
avatar: "https://cdn.discordapp.com/avatars/#{details['id']}/#{details['avatar']}",
|
|
65
|
+
rawData: details,
|
|
66
|
+
},
|
|
67
|
+
}.to_json,
|
|
68
|
+
})
|
|
69
|
+
raise("failed to add discord identity: #{response2.code} #{response2.message}\n#{response2.parsed_response.inspect}") if response2.code != 201
|
|
70
|
+
|
|
71
|
+
get_user(id) # force fresh fetch to ensure identity data is included properly
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def get_or_create_user(id)
|
|
75
|
+
get_user(id) || create_user(id)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Pages through every user in the tenant. Used by maintenance tasks (e.g. the
|
|
79
|
+
# dedupe_users rake task) - there's no discordId to filter by when scanning the
|
|
80
|
+
# whole user base for duplicates/orphans.
|
|
81
|
+
def list_users(page_size: 100)
|
|
82
|
+
users = []
|
|
83
|
+
page = 1
|
|
84
|
+
loop do
|
|
85
|
+
response = HTTParty.get("#{auth.server_url}/api/users", {
|
|
86
|
+
headers: { "Authorization" => "Bearer #{get_token}" },
|
|
87
|
+
query: { page: page, page_size: page_size },
|
|
88
|
+
})
|
|
89
|
+
raise("failed to list users: #{response.code} #{response.message}\n#{response.parsed_response.inspect}") if response.code != 200 || !response.parsed_response.is_a?(Array)
|
|
90
|
+
|
|
91
|
+
batch = response.parsed_response
|
|
92
|
+
users.concat(batch.map { |u| Auth::ApiUser.new(u) })
|
|
93
|
+
break if batch.length < page_size
|
|
94
|
+
|
|
95
|
+
page += 1
|
|
96
|
+
end
|
|
97
|
+
users
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def delete_user(logto_id)
|
|
101
|
+
response = HTTParty.delete("#{auth.server_url}/api/users/#{logto_id}", { headers: { "Authorization" => "Bearer #{get_token}" } })
|
|
102
|
+
return true if [204, 404].include?(response.code)
|
|
103
|
+
|
|
104
|
+
raise("failed to delete user #{logto_id}: #{response.code} #{response.message}\n#{response.parsed_response.inspect}")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def get_user_by_id(logto_id)
|
|
108
|
+
response = HTTParty.get("#{auth.server_url}/api/users/#{logto_id}", { headers: { "Authorization" => "Bearer #{get_token}" } })
|
|
109
|
+
return nil if response.code == 404
|
|
110
|
+
raise("failed to get user: #{response.code} #{response.message}\n#{response.parsed_response.inspect}") if response.code != 200
|
|
111
|
+
|
|
112
|
+
Auth::ApiUser.new(response.parsed_response)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def get_user_roles(logto_id)
|
|
116
|
+
response = HTTParty.get("#{auth.server_url}/api/users/#{logto_id}/roles", { headers: { "Authorization" => "Bearer #{get_token}" } })
|
|
117
|
+
raise("failed to get user roles: #{response.code} #{response.message}\n#{response.parsed_response.inspect}") if response.code != 200
|
|
118
|
+
|
|
119
|
+
response.parsed_response
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def get_role_scopes(role_id)
|
|
123
|
+
response = HTTParty.get("#{auth.server_url}/api/roles/#{role_id}/scopes", { headers: { "Authorization" => "Bearer #{get_token}" } })
|
|
124
|
+
raise("failed to get role scopes: #{response.code} #{response.message}\n#{response.parsed_response.inspect}") if response.code != 200
|
|
125
|
+
|
|
126
|
+
response.parsed_response
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def get_users_with_role(role_id)
|
|
130
|
+
response = HTTParty.get("#{auth.server_url}/api/roles/#{role_id}/users", { headers: { "Authorization" => "Bearer #{get_token}" } })
|
|
131
|
+
raise("failed to get users with role: #{response.code} #{response.message}\n#{response.parsed_response.inspect}") if response.code != 200
|
|
132
|
+
|
|
133
|
+
response.parsed_response
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require("active_job")
|
|
4
|
+
|
|
5
|
+
module YiffSpace
|
|
6
|
+
module Serializers
|
|
7
|
+
class AnonymousAuthInfoSerializer < ActiveJob::Serializers::ObjectSerializer
|
|
8
|
+
def serialize(_)
|
|
9
|
+
super({})
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def deserialize(_)
|
|
13
|
+
Auth::AuthInfo::Anonymous.instance
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def klass
|
|
19
|
+
Auth::AuthInfo::Anonymous
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require("active_job")
|
|
4
|
+
|
|
5
|
+
module YiffSpace
|
|
6
|
+
module Serializers
|
|
7
|
+
class AnonymousUserInfoSerializer < ActiveJob::Serializers::ObjectSerializer
|
|
8
|
+
def serialize(_)
|
|
9
|
+
super({})
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def deserialize(_)
|
|
13
|
+
Auth::UserInfo::Anonymous.instance
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def klass
|
|
19
|
+
Auth::UserInfo::Anonymous
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require("active_job")
|
|
4
|
+
|
|
5
|
+
module YiffSpace
|
|
6
|
+
module Serializers
|
|
7
|
+
class AuthInfoSerializer < ActiveJob::Serializers::ObjectSerializer
|
|
8
|
+
def serialize(arg)
|
|
9
|
+
super(**arg.serializable_hash)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def deserialize(arg)
|
|
13
|
+
Auth::AuthInfo.from_json(arg)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def klass
|
|
19
|
+
Auth::AuthInfo
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require("active_job")
|
|
4
|
+
|
|
5
|
+
module YiffSpace
|
|
6
|
+
module Serializers
|
|
7
|
+
class DiscordInfoSerializer < ActiveJob::Serializers::ObjectSerializer
|
|
8
|
+
def serialize(arg)
|
|
9
|
+
super(**arg.serializable_hash)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def deserialize(arg)
|
|
13
|
+
Auth::DiscordInfo.from_json(arg)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def klass
|
|
19
|
+
Auth::DiscordInfo
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require("active_job")
|
|
4
|
+
|
|
5
|
+
module YiffSpace
|
|
6
|
+
module Serializers
|
|
7
|
+
class PermissionsSerializer < ActiveJob::Serializers::ObjectSerializer
|
|
8
|
+
def serialize(arg)
|
|
9
|
+
super("value" => arg.value)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def deserialize(arg)
|
|
13
|
+
Auth::Permissions.new(arg["value"])
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def klass
|
|
19
|
+
Auth::Permissions
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require("active_job")
|
|
4
|
+
|
|
5
|
+
module YiffSpace
|
|
6
|
+
module Serializers
|
|
7
|
+
class UserInfoSerializer < ActiveJob::Serializers::ObjectSerializer
|
|
8
|
+
def serialize(arg)
|
|
9
|
+
super(**arg.serializable_hash)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def deserialize(arg)
|
|
13
|
+
Auth::UserInfo.from_json(arg)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def klass
|
|
19
|
+
Auth::UserInfo
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module YiffSpace
|
|
4
|
+
module Utils
|
|
5
|
+
# get_or_create_user is check-then-act (query Logto, then create) with no locking
|
|
6
|
+
# of its own - two concurrent calls for a discord id that doesn't exist yet will
|
|
7
|
+
# both create a user, and the loser's discord-identity-attach step fails once the
|
|
8
|
+
# winner's identity claims it first. That leaves an identity-less "orphan" user
|
|
9
|
+
# behind in Logto. This scans the whole user base to find those orphans (and any
|
|
10
|
+
# other duplicate/ambiguous state) so they can be cleaned up.
|
|
11
|
+
module UserDeduper
|
|
12
|
+
# create_user sets a new user's avatar to the discord CDN URL for the discord id
|
|
13
|
+
# it was created for, before the identity-attach step runs - so even an orphan
|
|
14
|
+
# that never got its identity attached still reveals which discord id it was
|
|
15
|
+
# meant for.
|
|
16
|
+
AVATAR_ID_PATTERN = %r{cdn\.discordapp\.com/avatars/(\d+)/}
|
|
17
|
+
|
|
18
|
+
module_function
|
|
19
|
+
|
|
20
|
+
def discord_id_for(user)
|
|
21
|
+
user.data.identities&.discord&.userId || avatar_discord_id_for(user)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def avatar_discord_id_for(user)
|
|
25
|
+
match = user.data.avatar.to_s.match(AVATAR_ID_PATTERN)
|
|
26
|
+
match && match[1]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def linked?(user)
|
|
30
|
+
user.data.identities&.discord&.userId.present?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Returns a hash with:
|
|
34
|
+
# - :conflicts - discord id => users, for ids where more than one user has the
|
|
35
|
+
# identity actually attached. Ambiguous; always needs manual review, never
|
|
36
|
+
# auto-deleted.
|
|
37
|
+
# - :deletable_orphans - [orphan, keeper] pairs, where orphan has no identity
|
|
38
|
+
# attached but its avatar points at a discord id claimed by exactly one other
|
|
39
|
+
# (linked) user. This is the confirmed race-loser case - safe to delete.
|
|
40
|
+
# - :unresolved_orphans - users with no identity attached and no corresponding
|
|
41
|
+
# linked user (still mid-race, or the winner can't be identified). Reported
|
|
42
|
+
# only, never auto-deleted.
|
|
43
|
+
def scan(management)
|
|
44
|
+
users = management.list_users
|
|
45
|
+
linked, unlinked = users.partition { |u| linked?(u) }
|
|
46
|
+
|
|
47
|
+
by_discord_id = linked.group_by { |u| u.data.identities.discord.userId }
|
|
48
|
+
conflicts = by_discord_id.select { |_id, group| group.length > 1 }
|
|
49
|
+
|
|
50
|
+
deletable_orphans = []
|
|
51
|
+
unresolved_orphans = []
|
|
52
|
+
unlinked.each do |user|
|
|
53
|
+
discord_id = avatar_discord_id_for(user)
|
|
54
|
+
keeper = discord_id && by_discord_id[discord_id]
|
|
55
|
+
if keeper&.length == 1
|
|
56
|
+
deletable_orphans << [user, keeper.first]
|
|
57
|
+
else
|
|
58
|
+
unresolved_orphans << user
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
{ conflicts: conflicts, deletable_orphans: deletable_orphans, unresolved_orphans: unresolved_orphans }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: yiffspace-auth
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Donovan_DMC
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-07-14 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: httparty
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.24'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.24'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: logto
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 0.2.0
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 0.2.0
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rails
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '7.1'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '7.1'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: yiffspace
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 0.1.0
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 0.1.0
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: zeitwerk
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '2.6'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '2.6'
|
|
82
|
+
description: Logto-based auth engine for https://yiff.space and related projects
|
|
83
|
+
email:
|
|
84
|
+
- hewwo@yiff.rocks
|
|
85
|
+
executables: []
|
|
86
|
+
extensions: []
|
|
87
|
+
extra_rdoc_files: []
|
|
88
|
+
files:
|
|
89
|
+
- CHANGELOG.md
|
|
90
|
+
- LICENSE
|
|
91
|
+
- README.md
|
|
92
|
+
- Rakefile
|
|
93
|
+
- app/controllers/yiff_space/auth/application_controller.rb
|
|
94
|
+
- app/controllers/yiff_space/auth/root_controller.rb
|
|
95
|
+
- app/controllers/yiff_space/auth/webhook_controller.rb
|
|
96
|
+
- app/helpers/yiff_space/auth_helper.rb
|
|
97
|
+
- app/views/yiff_space/auth/root/permissions.html.erb
|
|
98
|
+
- config/routes.rb
|
|
99
|
+
- lib/tasks/yiffspace_auth_tasks.rake
|
|
100
|
+
- lib/yiffspace/auth.rb
|
|
101
|
+
- lib/yiffspace/auth/api_user.rb
|
|
102
|
+
- lib/yiffspace/auth/auth_info.rb
|
|
103
|
+
- lib/yiffspace/auth/auth_info/anonymous.rb
|
|
104
|
+
- lib/yiffspace/auth/client.rb
|
|
105
|
+
- lib/yiffspace/auth/discord_info.rb
|
|
106
|
+
- lib/yiffspace/auth/engine.rb
|
|
107
|
+
- lib/yiffspace/auth/helper.rb
|
|
108
|
+
- lib/yiffspace/auth/permissions.rb
|
|
109
|
+
- lib/yiffspace/auth/set_client_name.rb
|
|
110
|
+
- lib/yiffspace/auth/user_info.rb
|
|
111
|
+
- lib/yiffspace/auth/user_info/anonymous.rb
|
|
112
|
+
- lib/yiffspace/auth/version.rb
|
|
113
|
+
- lib/yiffspace/core_ext/action_dispatch/set_auth_client.rb
|
|
114
|
+
- lib/yiffspace/core_ext/action_dispatch/set_auth_client/scoped.rb
|
|
115
|
+
- lib/yiffspace/core_ext/logto/named_session_storage.rb
|
|
116
|
+
- lib/yiffspace/extensions/action_dispatch/set_auth_client.rb
|
|
117
|
+
- lib/yiffspace/extensions/action_dispatch/set_auth_client/scoped.rb
|
|
118
|
+
- lib/yiffspace/extensions/logto/named_session_storage.rb
|
|
119
|
+
- lib/yiffspace/logto_management_client.rb
|
|
120
|
+
- lib/yiffspace/serializers/anonymous_auth_info_serializer.rb
|
|
121
|
+
- lib/yiffspace/serializers/anonymous_user_info_serializer.rb
|
|
122
|
+
- lib/yiffspace/serializers/auth_info_serializer.rb
|
|
123
|
+
- lib/yiffspace/serializers/discord_info_serializer.rb
|
|
124
|
+
- lib/yiffspace/serializers/permissions_serializer.rb
|
|
125
|
+
- lib/yiffspace/serializers/user_info_serializer.rb
|
|
126
|
+
- lib/yiffspace/utils/user_deduper.rb
|
|
127
|
+
homepage: https://yiff.space
|
|
128
|
+
licenses:
|
|
129
|
+
- MIT
|
|
130
|
+
metadata:
|
|
131
|
+
allowed_push_host: https://rubygems.org
|
|
132
|
+
homepage_uri: https://yiff.space
|
|
133
|
+
source_code_uri: https://github.com/YiffSpace/Auth.rb
|
|
134
|
+
changelog_uri: https://github.com/YiffSpace/Auth.rb/blob/master/CHANGELOG.md
|
|
135
|
+
rubygems_mfa_required: 'true'
|
|
136
|
+
rdoc_options: []
|
|
137
|
+
require_paths:
|
|
138
|
+
- lib
|
|
139
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
140
|
+
requirements:
|
|
141
|
+
- - ">="
|
|
142
|
+
- !ruby/object:Gem::Version
|
|
143
|
+
version: 3.4.1
|
|
144
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
|
+
requirements:
|
|
146
|
+
- - ">="
|
|
147
|
+
- !ruby/object:Gem::Version
|
|
148
|
+
version: '0'
|
|
149
|
+
requirements: []
|
|
150
|
+
rubygems_version: 3.6.2
|
|
151
|
+
specification_version: 4
|
|
152
|
+
summary: Logto-based auth engine for https://yiff.space and related projects
|
|
153
|
+
test_files: []
|