Module: TwoPass

Defined in:
lib/2pass.rb,
lib/2pass/version.rb

Constant Summary collapse

VAULT_DIR =
"#{Dir.home}/.2pass"
VERSION =
"2.1.0"

Class Method Summary collapse

Class Method Details

.add_secret(vault_name, id, value, env: nil) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/2pass.rb', line 82

def add_secret(vault_name, id, value, env: nil)
  new_secret = {
    id: id,
    value: value
  }
  data = load_vault(vault_name, env: env)
  existing_entry_id = data.find_index { |hash| hash[:id] == new_secret[:id] }
  if existing_entry_id
    raise "The secret already exists"
  end
  data << new_secret
  save_vault(vault_name, data, env: env)
end


18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/2pass.rb', line 18

def create_symlink(vault_name, target_path, env: nil)
  FileUtils.mkdir_p(VAULT_DIR)
  symlink_path = vault_file_path(vault_name, env: env)
  expanded_target_path = File.expand_path(target_path)

  unless File.exist?(expanded_target_path)
    raise "Target vault file does not exist: #{expanded_target_path}"
  end

  unless File.readable?(expanded_target_path)
    raise "Target vault file is not readable: #{expanded_target_path}"
  end

  if File.symlink?(symlink_path)
    existing_target = File.expand_path(File.readlink(symlink_path), File.dirname(symlink_path))
    if existing_target == expanded_target_path
      puts "Symlink already exists: #{symlink_path} -> #{expanded_target_path}"
      return
    end

    raise "A different symlink already exists at #{symlink_path}: #{existing_target}"
  end

  if File.exist?(symlink_path)
    raise "A regular file already exists at #{symlink_path}. Remove it before creating a symlink."
  end

  File.symlink(expanded_target_path, symlink_path)
  puts "Created symlink: #{symlink_path} -> #{expanded_target_path}"
end

.get_secret(vault_name, id, env: nil) ⇒ Object



96
97
98
99
100
101
102
103
104
# File 'lib/2pass.rb', line 96

def get_secret(vault_name, id, env: nil)
  vault = load_vault(vault_name, env: env)
  entry = vault.find { |hash| hash[:id] == id }
  if entry
    entry[:value]
  else
    raise "Entry not found"
  end
end

.list_content(vault_name, env: nil, format: :table) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/2pass.rb', line 55

def list_content(vault_name, env: nil, format: :table)
  vault = load_vault(vault_name, env: env)
  if format == :table
    rows = vault
      .sort_by { |hash| hash[:id] }
      .each_with_object([]) do |h, arr|
      arr << [h[:id], h[:value]]
    end
    Terminal::Table.new(
      rows: rows,
      title: env || nil,
      style: {all_separators: true}
    ).to_s
  elsif format == :dotenv
    vault
      .map { |h| "#{h[:id]}=#{h[:value]}" }
      .sort
      .join("\n")
  elsif format == :json
    JSON.pretty_generate(
      vault
        .map { |hash| hash.slice(:id, :value) }
        .sort_by { |hash| hash[:id] }
    )
  end
end

.save_vault(vault_name, data, env: nil) ⇒ Object



49
50
51
52
53
# File 'lib/2pass.rb', line 49

def save_vault(vault_name, data, env: nil)
  FileUtils.mkdir_p(VAULT_DIR)
  file_path = vault_file_path(vault_name, env: env)
  File.write(file_path, YAML.dump(data))
end

.update_cliObject



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/2pass.rb', line 106

def update_cli
  latest_version = fetch_latest_version
  current = Gem::Version.new(VERSION)
  latest = Gem::Version.new(latest_version)

  if current >= latest
    return "2pass is already up to date (#{VERSION})."
  end

  unless run_update_command
    raise "Failed to update 2pass. Try running: #{update_command.join(' ')}"
  end

  "Updated 2pass from #{VERSION} to #{latest_version}. Restart your shell session if needed."
end