ask-auth 0.1.4 → 0.2.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/lib/ask/auth/providers/rails_credentials.rb +19 -48
- data/lib/ask/auth/version.rb +1 -1
- data/lib/ask/auth.rb +33 -14
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bb2c28ee519b76814303a1c502ad2cd707bda862ea84ba1d9172656a07fb9c92
|
|
4
|
+
data.tar.gz: a1eb477ff8f9fb5a7e4b6e35e268e372644e1e0c596d32f622392d003e36ff63
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 523eb5835f6d43f808a0eed7c80ca1fb51c6e97c7f47ecfce9920b15edc5a9521edb6a42be99f17b9ebd5540bec4df0c6a562f9aabc3e45c378275dadf91ab68
|
|
7
|
+
data.tar.gz: 81e3eb0a8f88c908edf12942453a5156402f072b28a8de0cd776787ad7a94667dc9449411bfaa9a3c2622c56c7144dbcaf9a8b0c784c6142ff6f38b56703b639
|
|
@@ -5,28 +5,17 @@ module Ask
|
|
|
5
5
|
module Providers
|
|
6
6
|
# Resolves credentials from Rails encrypted credentials.
|
|
7
7
|
#
|
|
8
|
-
#
|
|
8
|
+
# Supports two lookup modes determined by the +name+ argument type:
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
10
|
+
# Symbol/String → flat key, looked up literally
|
|
11
|
+
# resolve(:opencode_api_key) → credentials.opencode_api_key
|
|
12
|
+
# resolve("opencode_api_key") → credentials.opencode_api_key
|
|
12
13
|
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# b. +Rails.application.credentials.opencode.api.key+ ← fully split
|
|
14
|
+
# Array → path segments, navigated in order
|
|
15
|
+
# resolve([:opencode, :api_key]) → credentials.opencode.api_key
|
|
16
|
+
# resolve([:opencode, :go, :api_key]) → credentials.opencode.go.api_key
|
|
17
17
|
#
|
|
18
|
-
#
|
|
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
|
|
22
|
-
#
|
|
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+.
|
|
18
|
+
# No splitting, no guessing. The caller determines the lookup path.
|
|
30
19
|
#
|
|
31
20
|
# Safely returns nil when Rails is not loaded.
|
|
32
21
|
class RailsCredentials
|
|
@@ -34,41 +23,23 @@ module Ask
|
|
|
34
23
|
return nil unless defined?(::Rails) && ::Rails.application.respond_to?(:credentials)
|
|
35
24
|
|
|
36
25
|
creds = ::Rails.application.credentials
|
|
37
|
-
|
|
38
|
-
parts
|
|
39
|
-
return nil if parts.empty?
|
|
40
|
-
|
|
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.
|
|
45
|
-
val = creds.public_send(s) rescue nil
|
|
46
|
-
return val if val
|
|
47
|
-
|
|
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)
|
|
60
|
-
end
|
|
61
|
-
next unless obj
|
|
26
|
+
parts = Array(name).map { |p| p.to_s.strip }
|
|
27
|
+
return nil if parts.empty? || parts.any?(&:empty?)
|
|
62
28
|
|
|
29
|
+
# Navigate the path segments. Must check the VALUE, not just
|
|
30
|
+
# respond_to?, because ActiveSupport::OrderedOptions#respond_to?
|
|
31
|
+
# returns true for any method name, even when the key doesn't exist.
|
|
32
|
+
value = parts.reduce(creds) do |obj, part|
|
|
63
33
|
begin
|
|
64
|
-
val = obj.public_send(
|
|
65
|
-
|
|
34
|
+
val = obj.public_send(part)
|
|
35
|
+
break nil if val.nil?
|
|
36
|
+
val
|
|
66
37
|
rescue NoMethodError
|
|
67
|
-
|
|
38
|
+
break nil
|
|
68
39
|
end
|
|
69
40
|
end
|
|
70
41
|
|
|
71
|
-
|
|
42
|
+
value
|
|
72
43
|
end
|
|
73
44
|
end
|
|
74
45
|
end
|
data/lib/ask/auth/version.rb
CHANGED
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(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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,38 @@ module Ask
|
|
|
49
51
|
end
|
|
50
52
|
|
|
51
53
|
# Walk providers in order and return the first non-nil credential.
|
|
52
|
-
#
|
|
54
|
+
# Tries each name in order and returns the first match.
|
|
55
|
+
#
|
|
56
|
+
# Each +name+ can be:
|
|
57
|
+
# Symbol/String → flat key, tried literally by all providers
|
|
58
|
+
# Array → path segments, used for nested lookups (e.g., Rails credentials)
|
|
59
|
+
#
|
|
60
|
+
# Ask::Auth.resolve(:openai_api_key) # flat key
|
|
61
|
+
# Ask::Auth.resolve(:opencode_api_key, :opencode_go_api_key) # fallbacks
|
|
62
|
+
# Ask::Auth.resolve([:opencode, :api_key]) # nested path: credentials.opencode.api_key
|
|
63
|
+
# Ask::Auth.resolve(:opencode_go_api_key, [:opencode, :api_key], user: current_user)
|
|
64
|
+
#
|
|
65
|
+
# +names+:: One or more Symbols, Strings, or Arrays identifying the credential
|
|
53
66
|
# +user+:: Optional user record for per-user providers (Database, OAuth)
|
|
54
|
-
def resolve(
|
|
55
|
-
|
|
56
|
-
return nil if name.empty?
|
|
67
|
+
def resolve(*names, user: nil)
|
|
68
|
+
return nil if names.empty?
|
|
57
69
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
70
|
+
names.each do |name|
|
|
71
|
+
# Validate the name
|
|
72
|
+
parts = Array(name).map { |p| p.to_s.strip }
|
|
73
|
+
next if parts.empty? || parts.any?(&:empty?)
|
|
61
74
|
|
|
62
|
-
|
|
63
|
-
|
|
75
|
+
configuration.providers.each do |provider|
|
|
76
|
+
# Pass the original form (Symbol/Array) so providers can interpret it
|
|
77
|
+
value = provider.call(name, user: user)
|
|
78
|
+
next if value.nil?
|
|
79
|
+
|
|
80
|
+
normalized = normalize(value)
|
|
81
|
+
return normalized unless normalized.nil?
|
|
82
|
+
end
|
|
64
83
|
end
|
|
65
84
|
|
|
66
|
-
raise MissingCredential,
|
|
85
|
+
raise MissingCredential, names
|
|
67
86
|
end
|
|
68
87
|
|
|
69
88
|
private
|