Class: Clacky::Mcp::OAuth::CredentialStore

Inherits:
Object
  • Object
show all
Defined in:
lib/clacky/mcp/oauth/credential_store.rb

Defined Under Namespace

Classes: Error

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(server_name:, home: Dir.home) ⇒ CredentialStore

Returns a new instance of CredentialStore.



15
16
17
18
19
20
21
# File 'lib/clacky/mcp/oauth/credential_store.rb', line 15

def initialize(server_name:, home: Dir.home)
  original_name = server_name.to_s
  safe_name = original_name.gsub(/[^a-zA-Z0-9_.-]/, "_")
  safe_name = "server" if safe_name.empty?
  safe_name = "#{safe_name}-#{Digest::SHA256.hexdigest(original_name)[0, 12]}" if safe_name != original_name
  @path = File.join(File.expand_path(home), ".clacky", "mcp", "oauth", "#{safe_name}.json")
end

Instance Attribute Details

#pathObject (readonly)

Returns the value of attribute path.



13
14
15
# File 'lib/clacky/mcp/oauth/credential_store.rb', line 13

def path
  @path
end

Instance Method Details

#deleteObject



53
54
55
56
57
58
# File 'lib/clacky/mcp/oauth/credential_store.rb', line 53

def delete
  return unless File.exist?(@path)

  ensure_directory
  with_lock(File::LOCK_EX) { FileUtils.rm_f(@path) }
end

#inspectObject



60
61
62
# File 'lib/clacky/mcp/oauth/credential_store.rb', line 60

def inspect
  "#<#{self.class} path=#{@path.inspect}>"
end

#loadObject



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/clacky/mcp/oauth/credential_store.rb', line 23

def load
  return nil unless File.file?(@path)

  with_lock(File::LOCK_SH) do
    value = JSON.parse(File.read(@path))
    raise JSON::ParserError unless value.is_a?(Hash)
    value
  end
rescue JSON::ParserError
  raise Error, "invalid OAuth credential file; log in again"
end

#save(value) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/clacky/mcp/oauth/credential_store.rb', line 35

def save(value)
  ensure_directory
  with_lock(File::LOCK_EX) do
    temporary = "#{@path}.tmp-#{Process.pid}-#{rand(1_000_000)}"
    File.open(temporary, File::WRONLY | File::CREAT | File::EXCL, 0o600) do |file|
      file.write(JSON.generate(value))
      file.write("\n")
      file.flush
      file.fsync
    end
    File.rename(temporary, @path)
    File.chmod(0o600, @path)
  ensure
    FileUtils.rm_f(temporary) if defined?(temporary) && temporary
  end
  value
end