ask-auth 0.1.3 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8535335b52d412f3c6cd8cb70de3547d5872c21e0aac5465c1bac88a5446847
4
- data.tar.gz: 1072dfab33c51e9ab57b6ccd928f97f9f55a947dd27d542696dece9330fa0326
3
+ metadata.gz: 899769786d686dad5733fb428f4947d077af5e4913573558890ac309dc246d7e
4
+ data.tar.gz: 8dad782619a917d310858ec90136b86638fdffe64e71060c72ba8af42f91a41f
5
5
  SHA512:
6
- metadata.gz: 349a4863d2061377b373402ea937e9fbaf15a8928662ec3af72e7754fc3e87542bf2e62e9355fbe90140e1a61c4d5e77985b1c87a706eddda8cf8abcd4599fe2
7
- data.tar.gz: b01eabafd424f1bf3348b55f05678b6ff6aeb20b805e870cf975631d400915e992d05f64799c74e9cc53a0a37e16db332b1a9bac096be25fd413df0acd8bb100
6
+ metadata.gz: 69064653971ef660f7c2aa2f297f5a1119888a18b5521ebee9c4bbf1058f474ffb70275d9cf60dec044ee94565520d9e0fc9defc782ee4b74505e17b01d4a6c4
7
+ data.tar.gz: e819dd4a9a9bac4a5d9cca0064d750610b16c8d1baf3de33cd4bb792ec3292279091a271e0c34ccfa85738ff228cd798384e503ef4cd909aae96b4e66b118fca
@@ -5,21 +5,28 @@ module Ask
5
5
  module Providers
6
6
  # Resolves credentials from Rails encrypted credentials.
7
7
  #
8
- # Convention: +resolve(:github_token)+ looks up multiple paths in order:
8
+ # Tries multiple strategies in order, from least to most nested:
9
9
  #
10
- # 1. Full name as a single top-level key (flat layout):
11
- # +Rails.application.credentials.github_token+
10
+ # 1. Full name as a single top-level key (flat layout)
11
+ # +resolve(:openai_api_key)+ → +Rails.application.credentials.openai_api_key+
12
12
  #
13
- # 2. First segment as namespace, joined rest as sub-key (nested layout):
14
- # +Rails.application.credentials.github.token+
13
+ # 2. Progressively split into prefix + joined suffix
14
+ # +resolve(:opencode_api_key)+ → tries:
15
+ # a. +Rails.application.credentials.opencode.api_key+ ← most common
16
+ # b. +Rails.application.credentials.opencode.api.key+ ← fully split
15
17
  #
16
- # 3. Fully split path (deeply nested layout):
17
- # +Rails.application.credentials.github.api.key+
18
+ # 3. For compound provider slugs, +resolve(:opencode_go_api_key)+ tries:
19
+ # a. +Rails.application.credentials.opencode.go_api_key+ ← slug as single key
20
+ # b. +Rails.application.credentials.opencode.go.api_key+ ← slug + api_key
21
+ # c. +Rails.application.credentials.opencode.go.api.key+ ← fully split
18
22
  #
19
- # Strategy 2 handles compound key names like +api_key+ correctly.
20
- # For +resolve(:opencode_api_key)+, it looks up:
21
- # +Rails.application.credentials.opencode.api_key+
22
- # instead of breaking +api_key+ into +api+ and +key+.
23
+ # 4. For deeply nested, +resolve(:nvidia_nim_api_key)+ tries:
24
+ # a. +Rails.application.credentials.nvidia.nim_api_key+ ← nvidia + nim_api_key
25
+ # b. +Rails.application.credentials.nvidia.nim.api_key+ ← nvidia.nim + api_key
26
+ # c. +Rails.application.credentials.nvidia.nim.api.key+ fully split
27
+ #
28
+ # Strategy 2b/3b/4b keep +api_key+ intact as a single credential key
29
+ # instead of breaking it into +api+ and +key+.
23
30
  #
24
31
  # Safely returns nil when Rails is not loaded.
25
32
  class RailsCredentials
@@ -29,33 +36,39 @@ module Ask
29
36
  creds = ::Rails.application.credentials
30
37
  s = name.to_s
31
38
  parts = s.split("_")
39
+ return nil if parts.empty?
32
40
 
33
- # 1. Full name as a single top-level key (e.g., credentials.opencode_api_key)
34
- # Must check the returned value, not just respond_to?, because
35
- # ActiveSupport::OrderedOptions#respond_to? returns true for any
36
- # method name even when the key doesn't exist in the hash.
41
+ # 1. Full name as a single top-level key
42
+ # Must check the value, not just respond_to?, because
43
+ # ActiveSupport::OrderedOptions#respond_to? returns true
44
+ # for any method name, even when the key doesn't exist.
37
45
  val = creds.public_send(s) rescue nil
38
46
  return val if val
39
47
 
40
- # 2. First segment as namespace, joined rest as sub-key
41
- # e.g., opencode_api_key credentials.opencode.api_key
42
- if parts.length >= 2
43
- first = parts.first
44
- rest = parts.drop(1).join("_")
45
- if creds.respond_to?(first)
46
- obj = creds.public_send(first)
47
- return obj.public_send(rest) if obj.respond_to?(rest)
48
+ # 2. Try progressively longer prefixes, keeping the remainder
49
+ # joined as a single key name at each level.
50
+ # split_at=1: parts[0] as prefix, parts[1..] joined as key
51
+ # split_at=2: parts[0..1] as prefix, parts[2..] joined as key
52
+ # etc.
53
+ (1...parts.length).each do |split_at|
54
+ prefix_parts = parts.take(split_at)
55
+ key = parts.drop(split_at).join("_")
56
+
57
+ obj = prefix_parts.reduce(creds) do |o, p|
58
+ break nil unless o.respond_to?(p)
59
+ o.public_send(p)
48
60
  end
49
- end
61
+ next unless obj
50
62
 
51
- # 3. Fully split path (original behavior)
52
- # e.g., opencode.go.api_key → credentials.opencode.go.api_key
53
- value = parts.reduce(creds) do |obj, part|
54
- break nil unless obj.respond_to?(part)
55
- obj.public_send(part)
63
+ begin
64
+ val = obj.public_send(key)
65
+ return val if val
66
+ rescue NoMethodError
67
+ # respond_to? can lie for some credential objects
68
+ end
56
69
  end
57
70
 
58
- value
71
+ nil
59
72
  end
60
73
  end
61
74
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ask
4
4
  module Auth
5
- VERSION = "0.1.3"
5
+ VERSION = "0.2.0"
6
6
  end
7
7
  end
data/lib/ask/auth.rb CHANGED
@@ -18,10 +18,12 @@ module Ask
18
18
  #
19
19
  module Auth
20
20
  class MissingCredential < KeyError
21
- def initialize(name)
22
- super("No credential found for #{name.inspect}. " \
23
- "Set #{name.to_s.upcase} in your environment, add it to ~/.ask/credentials.yml, " \
24
- "or configure a provider.")
21
+ def initialize(names)
22
+ names = Array(names).flatten
23
+ inspected = names.map(&:inspect).join(", ")
24
+ super("No credential found for #{inspected}. " \
25
+ "Set one of #{names.map { |n| n.to_s.upcase }.uniq.join(", ")} in your environment, " \
26
+ "add it to ~/.ask/credentials.yml, or configure a provider.")
25
27
  end
26
28
  end
27
29
 
@@ -49,21 +51,29 @@ module Ask
49
51
  end
50
52
 
51
53
  # Walk providers in order and return the first non-nil credential.
52
- # +name+:: Symbol or String identifying the credential (e.g. +:github_token+)
54
+ # Accepts one or more credential names tries each in order,
55
+ # returning the first match across all providers.
56
+ #
57
+ # Ask::Auth.resolve(:openai_api_key)
58
+ # Ask::Auth.resolve(:opencode_go_api_key, :opencode_api_key, user: current_user)
59
+ #
60
+ # +names+:: One or more Symbols or Strings identifying the credential
53
61
  # +user+:: Optional user record for per-user providers (Database, OAuth)
54
- def resolve(name, user: nil)
55
- name = name.to_s.strip
56
- return nil if name.empty?
62
+ def resolve(*names, user: nil)
63
+ names = names.flatten.map { |n| n.to_s.strip }.reject(&:empty?)
64
+ return nil if names.empty?
57
65
 
58
- configuration.providers.each do |provider|
59
- value = provider.call(name, user: user)
60
- next if value.nil?
66
+ names.each do |name|
67
+ configuration.providers.each do |provider|
68
+ value = provider.call(name, user: user)
69
+ next if value.nil?
61
70
 
62
- normalized = normalize(value)
63
- return normalized unless normalized.nil?
71
+ normalized = normalize(value)
72
+ return normalized unless normalized.nil?
73
+ end
64
74
  end
65
75
 
66
- raise MissingCredential, name
76
+ raise MissingCredential, names
67
77
  end
68
78
 
69
79
  private
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ask-auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto