Problem: the CSES API token was packed into the git credential
password field using a tab separator. This leaked session state
into the credential layer and broke with helpers that don't
differentiate by path (e.g. `cache`).
Solution: store the CSES token via `save_platform_cookies`/
`load_platform_cookies`, the same mechanism every other platform
uses for session state. Remove all token logic from
`git_credential.lua`. Git credential now stores only
`{username, password}` uniformly for all platforms.
107 lines
2.5 KiB
Lua
107 lines
2.5 KiB
Lua
---@class cp.Credentials
|
|
---@field username string
|
|
---@field password string
|
|
|
|
local M = {}
|
|
|
|
local HOSTS = {
|
|
atcoder = 'atcoder.jp',
|
|
codechef = 'www.codechef.com',
|
|
codeforces = 'codeforces.com',
|
|
cses = 'cses.fi',
|
|
kattis = 'open.kattis.com',
|
|
usaco = 'usaco.org',
|
|
}
|
|
|
|
local _helper_checked = false
|
|
local _helper_ok = false
|
|
|
|
---@return boolean
|
|
function M.has_helper()
|
|
if not _helper_checked then
|
|
local r = vim
|
|
.system({ 'git', 'config', 'credential.helper' }, { text = true, timeout = 5000 })
|
|
:wait()
|
|
_helper_ok = r.code == 0 and r.stdout ~= nil and vim.trim(r.stdout) ~= ''
|
|
_helper_checked = true
|
|
end
|
|
return _helper_ok
|
|
end
|
|
|
|
---@param host string
|
|
---@param extra? table<string, string>
|
|
---@return string
|
|
local function _build_input(host, extra)
|
|
local lines = { 'protocol=https', 'host=' .. host }
|
|
if extra then
|
|
for k, v in pairs(extra) do
|
|
table.insert(lines, k .. '=' .. v)
|
|
end
|
|
end
|
|
table.insert(lines, '')
|
|
table.insert(lines, '')
|
|
return table.concat(lines, '\n')
|
|
end
|
|
|
|
---@param stdout string
|
|
---@return table<string, string>
|
|
local function _parse_output(stdout)
|
|
local result = {}
|
|
for line in stdout:gmatch('[^\n]+') do
|
|
local k, v = line:match('^(%S+)=(.+)$')
|
|
if k and v then
|
|
result[k] = v
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
---@param platform string
|
|
---@return cp.Credentials?
|
|
function M.get(platform)
|
|
local host = HOSTS[platform]
|
|
if not host then
|
|
return nil
|
|
end
|
|
|
|
local input = _build_input(host)
|
|
local obj = vim
|
|
.system({ 'git', 'credential', 'fill' }, { stdin = input, text = true, timeout = 5000 })
|
|
:wait()
|
|
if obj.code ~= 0 then
|
|
return nil
|
|
end
|
|
|
|
local parsed = _parse_output(obj.stdout or '')
|
|
if not parsed.username or not parsed.password then
|
|
return nil
|
|
end
|
|
|
|
return { username = parsed.username, password = parsed.password }
|
|
end
|
|
|
|
---@param platform string
|
|
---@param creds cp.Credentials
|
|
function M.store(platform, creds)
|
|
local host = HOSTS[platform]
|
|
if not host then
|
|
return
|
|
end
|
|
|
|
local input = _build_input(host, { username = creds.username, password = creds.password })
|
|
vim.system({ 'git', 'credential', 'approve' }, { stdin = input, text = true }):wait()
|
|
end
|
|
|
|
---@param platform string
|
|
---@param creds cp.Credentials
|
|
function M.reject(platform, creds)
|
|
local host = HOSTS[platform]
|
|
if not host or not creds then
|
|
return
|
|
end
|
|
|
|
local input = _build_input(host, { username = creds.username, password = creds.password })
|
|
vim.system({ 'git', 'credential', 'reject' }, { stdin = input, text = true }):wait()
|
|
end
|
|
|
|
return M
|