yiffspace 0.0.10 → 0.0.12

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f112970fd5ee72b9230c1b3236a3831671bb2dfedd098b4e00a061ebfcff6cf1
4
- data.tar.gz: 71c2f7b6b44f0988c00716882dc45c0da80ad7d8b4fa63f8e12aa9d807971b68
3
+ metadata.gz: db133305de79cf1a94b029e1e149359cd4b94764850b020316e7c14be52a4104
4
+ data.tar.gz: e9add13312cffed633e8ccb8594843b0468d8dc8a691bdc8910bc8df05ccb9a9
5
5
  SHA512:
6
- metadata.gz: 5321ef4adb4ac4cf98aac76492bd1ae15c8bd98efcf91e65a39fa39f1e06c32e5153a12650ecee959269a1b0d069ac83590dd758f4809ef8166566caf8be70d2
7
- data.tar.gz: bf65d46d86952bdcd8006a821f69220607eec81fc8da392b9a9dbc699976c698ab38e3fe922a577eea480d5e8c928aa0873494c1f697371e51b9b0fcdd6adb76
6
+ metadata.gz: 78c7fcfe985b6f98b9ff42d0cbff09b75442443ab950ac6e013a62bdbf23aca5452f9bde3c8248fa7e10635254b2b5746dd6c6cd4858a58433463556b9a3ba28
7
+ data.tar.gz: 1dead8a526b3c5325b79b0c351183d4813c6c4d42d9158d7308376dd05eaf5bcfc633d36abd4c9c6873612bb24e1cabda5306d0e7418085e307a54bbef460a0f
@@ -12,8 +12,8 @@ module YiffSpace
12
12
  def cb
13
13
  client.handle_sign_in_callback(url: request.original_url)
14
14
  user = client.fetch_user_info
15
- token = client.access_token_claims(resource: auth_client_config.resource)
16
15
  self.user = UserInfo.new(id: user["identities"]["discord"]["userId"], user: user, discord: user["identities"]["discord"]["details"]["rawData"])
16
+ token = client.access_token_claims(resource: auth_client_config.resource)
17
17
  self.auth = AuthInfo.new(id: user["identities"]["discord"]["userId"], token: token, permissions: token["scope"].split, roles: user["roles"], client_id: auth_client_config.client_id)
18
18
  end
19
19
 
@@ -21,7 +21,7 @@ module YiffSpace
21
21
 
22
22
  def logout
23
23
  client.sign_out(post_logout_redirect_uri: request.base_url)
24
- reset_user!
24
+ full_reset!
25
25
  end
26
26
 
27
27
  def debug
@@ -33,7 +33,7 @@ module YiffSpace
33
33
  session: session,
34
34
  client: auth_client_config.as_json.merge(client_secret: "[REDACTED]"),
35
35
  user: client.fetch_user_info,
36
- token: client.access_token_claims(resource: "https://gallery.furry.cool"),
36
+ token: client.access_token_claims(resource: auth_client_config.resource),
37
37
  })
38
38
  end
39
39
 
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YiffSpace
4
+ module Auth
5
+ class ApiUser
6
+ attr_reader(:data)
7
+
8
+ def initialize(data)
9
+ @data = Utils::OpenHash.from(data)
10
+ end
11
+
12
+ delegate(:id, to: :data)
13
+
14
+ def discord_id
15
+ data.identities.discord.userId
16
+ end
17
+
18
+ def created_at
19
+ Time.zone.at(data.createdAt)
20
+ end
21
+
22
+ def updated_at
23
+ Time.zone.at(data.updatedAt)
24
+ end
25
+
26
+ def last_sign_in_at
27
+ data.lastSignInAt.nil? ? nil : Time.zone.at(data.lastSignInAt)
28
+ end
29
+
30
+ def discord
31
+ data.identities.discord.details.blank? ? nil : DiscordInfo.new(data.identities.discord.details.rawData)
32
+ end
33
+
34
+ def avatar
35
+ Images::Avatar.get_for(discord_id, :discord)
36
+ end
37
+
38
+ def banner
39
+ Images::Banner.get_for(discord_id, :discord)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative("../core_ext/logto/named_session_storage")
4
+ require("httparty")
4
5
 
5
6
  module YiffSpace
6
7
  module Auth
@@ -13,7 +14,7 @@ module YiffSpace
13
14
 
14
15
  def initialize(name)
15
16
  @name = name.to_sym
16
- @scopes = %i[openid offline_access profile roles identities]
17
+ @scopes = %i[openid offline_access profile roles identities custom_data]
17
18
  @permissions = []
18
19
  @redirect_uri = "http://127.0.0.1:3000/auth/cb"
19
20
  @server_url = "https://auth.yiff.space"
@@ -38,6 +39,18 @@ module YiffSpace
38
39
  storage: LogtoClient::NamedSessionStorage.new("yiffspace", controller.session, app_id: @name),
39
40
  )
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
41
54
  end
42
55
  end
43
56
  end
@@ -3,13 +3,15 @@
3
3
  module YiffSpace
4
4
  module Auth
5
5
  class DiscordInfo
6
- ATTRIBUTES = %i[id flags avatar banner locale username avatar_url banner_url global_name mfa_enabled public_flags discriminator].freeze
7
- attr_reader(*ATTRIBUTES)
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)
8
10
 
9
11
  def initialize(options = {}, **kwargs)
10
- options = options.merge(kwargs).with_indifferent_access
12
+ @data = options.merge(kwargs).with_indifferent_access
11
13
  ATTRIBUTES.each do |attr|
12
- instance_variable_set("@#{attr}", options[attr])
14
+ instance_variable_set("@#{attr}", @data[attr])
13
15
  end
14
16
 
15
17
  return unless YiffSpace.config.auth.update_discord_images
@@ -31,6 +31,12 @@ module YiffSpace
31
31
  # The `last_ip_addr` attribute of the User model, used by the UserResolvableMethods concern
32
32
  attr_accessor(:last_ip_addr_attribute)
33
33
 
34
+ # Logto Management API credentials (shared across all auth clients).
35
+ attr_accessor(:logto_api_client_id, :logto_api_client_secret, :logto_api_resource)
36
+
37
+ # Discord bot token used to look up Discord users (shared across all auth clients).
38
+ attr_accessor(:discord_bot_token)
39
+
34
40
  # The anonymous user's name, can be a proc
35
41
  attr_reader(:anonymous_user_name)
36
42
 
@@ -43,15 +49,19 @@ module YiffSpace
43
49
  attr_writer(:system_user_getter)
44
50
 
45
51
  def initialize
46
- @max_multi_count = -> { 100 }
47
- @redis_url = -> {}
48
- @user_class = nil
49
- @user_like_class = nil
50
- @user_resolvable_class = nil
51
- @current_class = nil
52
- @default_ip_address = "127.0.0.1"
53
- @last_ip_addr_attribute = :last_ip_addr
54
- @anonymous_user_name = -> { "Anonymous" }
52
+ @max_multi_count = -> { 100 }
53
+ @redis_url = -> {}
54
+ @user_class = nil
55
+ @user_like_class = nil
56
+ @user_resolvable_class = nil
57
+ @current_class = nil
58
+ @default_ip_address = "127.0.0.1"
59
+ @last_ip_addr_attribute = :last_ip_addr
60
+ @anonymous_user_name = -> { "Anonymous" }
61
+ @logto_api_client_id = nil
62
+ @logto_api_client_secret = nil
63
+ @logto_api_resource = nil
64
+ @discord_bot_token = nil
55
65
  end
56
66
 
57
67
  def user_class
@@ -0,0 +1,78 @@
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']}", username: 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
+ Auth::ApiUser.new(response.parsed_response)
72
+ end
73
+
74
+ def get_or_create_user(id)
75
+ get_user(id) || create_user(id)
76
+ end
77
+ end
78
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module YiffSpace
4
- VERSION = "0.0.10"
4
+ VERSION = "0.0.12"
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yiffspace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.10
4
+ version: 0.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Donovan_DMC
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-06-17 00:00:00.000000000 Z
10
+ date: 2026-06-21 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: abbrev
@@ -119,6 +119,7 @@ files:
119
119
  - lib/tasks/yiff_space_tasks.rake
120
120
  - lib/yiffspace.rb
121
121
  - lib/yiffspace/auth.rb
122
+ - lib/yiffspace/auth/api_user.rb
122
123
  - lib/yiffspace/auth/auth_info.rb
123
124
  - lib/yiffspace/auth/auth_info/anonymous.rb
124
125
  - lib/yiffspace/auth/client.rb
@@ -201,6 +202,7 @@ files:
201
202
  - lib/yiffspace/include/user_attribute.rb
202
203
  - lib/yiffspace/include/user_like.rb
203
204
  - lib/yiffspace/include/user_resolvable.rb
205
+ - lib/yiffspace/logto_management_client.rb
204
206
  - lib/yiffspace/railtie.rb
205
207
  - lib/yiffspace/search/query_builder.rb
206
208
  - lib/yiffspace/search/query_dsl.rb