Add bash completion for pkica
Static completion rules for all pkica commands, options and option values, plus unit, interactive (tmux) and --help reconciliation tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
81404a24cc
commit
11283c573a
@@ -0,0 +1,82 @@
|
||||
# Автодополнение bash для `pkica`
|
||||
|
||||
`pkica.bash-completion` — правила автодополнения (TAB) в bash для утилиты `pkica`
|
||||
(командная строка управления инфраструктурой КриптоПро УЦ 2.0).
|
||||
|
||||
## Установка
|
||||
|
||||
Требуется пакет `bash-completion`.
|
||||
|
||||
Для всех пользователей:
|
||||
|
||||
```bash
|
||||
sudo install -m 644 pkica.bash-completion /usr/share/bash-completion/completions/pkica
|
||||
```
|
||||
|
||||
Для текущего пользователя:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.local/share/bash-completion/completions
|
||||
cp pkica.bash-completion ~/.local/share/bash-completion/completions/pkica
|
||||
```
|
||||
|
||||
Для текущей сессии:
|
||||
|
||||
```bash
|
||||
source pkica.bash-completion
|
||||
```
|
||||
|
||||
После установки откройте новый терминал. Дополнение работает для `pkica`
|
||||
из `PATH` и для запуска по пути (`./pkica`, `/opt/.../pkica`).
|
||||
|
||||
## Что дополняется
|
||||
|
||||
* команды и подкоманды всех уровней (`pkica ca signing-key change-usage`);
|
||||
* опции текущей команды: после `-` — короткие и длинные, без `-` — только длинные.
|
||||
Уже указанные опции (и их синонимы) повторно не предлагаются;
|
||||
* значения опций с фиксированным набором значений:
|
||||
|
||||
| Опция | Значения |
|
||||
| --- | --- |
|
||||
| `--format` | `Json`, `Yaml` |
|
||||
| `-s`, `--storage-type` | `PostgreSql`, `SqlServer` |
|
||||
| `--sign-tool-class` | `KC1` … `KA1` |
|
||||
| `-k`, `--key-spec` | `Signature`, `KeyExchange` |
|
||||
| `--key-location`, `--store-location` | `CurrentUser`, `LocalMachine` |
|
||||
| `--store-name` | `My`, `Root`, `CertificateAuthority`, … |
|
||||
| `--serial-number-algorithm` | `CryptoPro`, `Msca` |
|
||||
| `-p`, `--provider-type` | `80`, `81` |
|
||||
| `-u`, `--usage` (signing-key) | `SignCertAndCrl`, `SignCrl`, `Retired` |
|
||||
| `-d`, `--default-template` | `SubCa`, `User`, `Tls`, `Ocsp`, `Tsp`, `Qual`, `QualOcsp`, `QualTsp` |
|
||||
| `-v`, `--value` (identification-kind) | `Personal`, `RemoteCert`, `RemotePassport`, `RemoteSystem` |
|
||||
| `-n`, `--name` (ra settings) | `NamePolicy`, `Templates`, `DefaultAuthority` |
|
||||
| `-v`, `--validity`, `-V`, `--key-validity` | `Years:`, `Months:`, `Weeks:`, `Days:`, `Hours:`, `Minutes:` |
|
||||
|
||||
* пути к файлам: `-f`/`--file`, `-o`/`--out-file`, `--extensions-file`,
|
||||
`--tls-cert-file`, `--signing-cert-file`; каталоги: `-o`/`--output-folder`
|
||||
(`ca crl get`, `ca crl issue`);
|
||||
* поддерживается форма `--опция=значение`.
|
||||
|
||||
Одна и та же короткая опция в разных командах может означать разное
|
||||
(например, `-f` в `ra operator add` — это `--first-name`, а не файл) — это учитывается.
|
||||
|
||||
## Ограничения
|
||||
|
||||
* Утилита при дополнении не вызывается: дерево команд зашито в файл
|
||||
(запуск .NET-приложения занимает заметное время).
|
||||
* Имена экземпляров ЦС, шаблонов, папок, групп, операторов не дополняются —
|
||||
для этого нужен доступ к БД.
|
||||
|
||||
## Тесты
|
||||
|
||||
Запуск в Linux или WSL из каталога `bash-completion`:
|
||||
|
||||
```bash
|
||||
bash tests/test_completion.sh # модульные тесты функции дополнения
|
||||
bash tests/test_interactive.sh # реальный TAB в bash через tmux (нужен tmux)
|
||||
bash tests/check_tree.sh [pkica] # сверка с выводом `pkica ... --help`
|
||||
```
|
||||
|
||||
`check_tree.sh` рекурсивно обходит справку утилиты (без аргумента берётся `pkica`
|
||||
из `PATH`) и сообщает о расхождениях команд и опций с таблицами в
|
||||
`pkica.bash-completion`. Запускайте его после обновления `pkica`.
|
||||
@@ -0,0 +1,251 @@
|
||||
# bash completion for pkica (КриптоПро УЦ: командная строка управления инфраструктурой УЦ)
|
||||
#
|
||||
# Установка:
|
||||
# sudo install -m 644 pkica.bash-completion /usr/share/bash-completion/completions/pkica
|
||||
# или для текущего пользователя:
|
||||
# mkdir -p ~/.local/share/bash-completion/completions
|
||||
# cp pkica.bash-completion ~/.local/share/bash-completion/completions/pkica
|
||||
# или разово:
|
||||
# source pkica.bash-completion
|
||||
#
|
||||
# Дерево команд и опций снято с `pkica ... --help`. Утилита при дополнении
|
||||
# не вызывается (запуск .NET-приложения слишком медленный).
|
||||
|
||||
# Подкоманды узлов дерева. Ключ - путь команды.
|
||||
declare -gA _pkica_cmds=(
|
||||
[pkica]="ca cert-registry ra"
|
||||
[pkica ca]="authority cert cert-request crl db import-legacy issue-cert new-cert-req new-root-cert rev-request signing-key template"
|
||||
[pkica ca authority]="export-settings import-settings list new remove set-default show"
|
||||
[pkica ca cert]="get list"
|
||||
[pkica ca cert-request]="get list"
|
||||
[pkica ca crl]="get issue schedule"
|
||||
[pkica ca crl schedule]="get update"
|
||||
[pkica ca db]="info new upgrade"
|
||||
[pkica ca rev-request]="get list"
|
||||
[pkica ca signing-key]="add change-usage list load replace show unload"
|
||||
[pkica ca template]="add encode-extension get list remove update"
|
||||
[pkica ca template encode-extension]="certificate-template identification-kind"
|
||||
[pkica cert-registry]="db import-legacy"
|
||||
[pkica cert-registry db]="info new upgrade"
|
||||
[pkica ra]="db folder group import-legacy operator permission settings"
|
||||
[pkica ra db]="info new upgrade"
|
||||
[pkica ra folder]="add delete get list rename"
|
||||
[pkica ra group]="add add-operator get list remove-operator rename"
|
||||
[pkica ra operator]="activate add deactivate get list update"
|
||||
[pkica ra permission]="get grant revoke"
|
||||
[pkica ra settings]="get set"
|
||||
)
|
||||
|
||||
# Опции команд. Синонимы разделены "|", "=" в конце - опция принимает значение.
|
||||
# Для узлов, отсутствующих в таблице, используется "-h|--help".
|
||||
declare -gA _pkica_opts=(
|
||||
[pkica]="-v|--version -h|--help"
|
||||
[pkica ca authority export-settings]="-n|--name= -f|--file= -h|--help --format="
|
||||
[pkica ca authority import-settings]="-n|--name= -f|--file= -h|--help --format="
|
||||
[pkica ca authority list]="-h|--help --format="
|
||||
[pkica ca authority new]="-n|--name= -h|--help"
|
||||
[pkica ca authority remove]="-n|--name= -h|--help"
|
||||
[pkica ca authority set-default]="-n|--name= --none -h|--help"
|
||||
[pkica ca authority show]="-n|--name= -h|--help --format="
|
||||
[pkica ca cert get]="-i|--id= -s|--serial-number= -f|--file= --force -h|--help --format="
|
||||
[pkica ca cert list]="--from= --to= --take= --oldest-first -h|--help --format="
|
||||
[pkica ca cert-request get]="-i|--id= -f|--file= --force -h|--help --format="
|
||||
[pkica ca cert-request list]="--from= --to= --take= --oldest-first -h|--help --format="
|
||||
[pkica ca crl get]="-n|--name= -v|--valid-at= --all -o|--output-folder= --force -h|--help"
|
||||
[pkica ca crl issue]="-n|--name= -d|--delta-only -u|--next-update= --final --key-index= -o|--output-folder= --force -h|--help"
|
||||
[pkica ca crl schedule get]="-n|--name= -h|--help"
|
||||
[pkica ca crl schedule update]="-n|--name= -b|--base= -d|--delta= -h|--help"
|
||||
[pkica ca db info]="-h|--help --format="
|
||||
[pkica ca db new]="-s|--storage-type= -c|--connection-string= --command-timeout= -b|--begin-use-empty --force -h|--help"
|
||||
[pkica ca db upgrade]="--command-timeout= -h|--help"
|
||||
[pkica ca import-legacy]="-c|--connection-string= -t|--timeout= -h|--help"
|
||||
[pkica ca issue-cert]="-f|--file= -o|--out-file= -n|--name= -t|--timeout= -h|--help"
|
||||
[pkica ca new-cert-req]="-o|--out-file= -t|--template= --template-major-version= --template-minor-version= --is-ca --path-length= -i|--key-index= --subject-sign-tool= --sign-tool-class= --extensions-file= -s|--subject= -c|--container-name= --hash-algorithm= -l|--key-length= --use-existing-key -e|--exportable --prompt-for-media -p|--provider-type= -n|--provider-name= --pin= --no-pin -k|--key-spec= --key-location= --silent -h|--help"
|
||||
[pkica ca new-root-cert]="-o|--out-file= -v|--validity= -V|--key-validity= --path-length= -i|--key-index= --extensions-file= --serial-number-algorithm= -s|--subject= -c|--container-name= --hash-algorithm= -l|--key-length= --use-existing-key -e|--exportable --prompt-for-media -p|--provider-type= -n|--provider-name= --pin= --no-pin -k|--key-spec= --key-location= --silent -h|--help"
|
||||
[pkica ca rev-request get]="-i|--id= -f|--file= --force -h|--help --format="
|
||||
[pkica ca rev-request list]="--from= --to= --take= --oldest-first -h|--help --format="
|
||||
[pkica ca signing-key add]="-n|--name= -t|--thumbprint= --store-name= --store-location= -u|--usage= --hash-algorithm= --accept-new-subject --planned-key-change-date= --planned-retirement-date= --pin= --no-pin --skip-container-check -h|--help"
|
||||
[pkica ca signing-key change-usage]="-n|--name= -i|--index= -u|--usage= --force -h|--help"
|
||||
[pkica ca signing-key list]="-n|--name= -l|--loaded --status --timeout= -h|--help --format="
|
||||
[pkica ca signing-key load]="-n|--name= -i|--index= --pin= --no-pin -u|--usage= -h|--help"
|
||||
[pkica ca signing-key replace]="-n|--name= -i|--index= -t|--thumbprint= --store-name= --store-location= --hash-algorithm= --accept-new-subject --planned-key-change-date= --planned-retirement-date= --pin= --no-pin --skip-container-check -h|--help"
|
||||
[pkica ca signing-key show]="-n|--name= -i|--index= --status --timeout= -h|--help --format="
|
||||
[pkica ca signing-key unload]="-n|--name= -i|--index= -h|--help"
|
||||
[pkica ca template add]="-f|--file= -d|--default-template= -h|--help"
|
||||
[pkica ca template encode-extension certificate-template]="-n|--name= -f|--file= --force -h|--help"
|
||||
[pkica ca template encode-extension identification-kind]="-v|--value= -f|--file= --force -h|--help"
|
||||
[pkica ca template get]="-n|--name= -f|--file= -h|--help"
|
||||
[pkica ca template list]="-f|--file= -d|--detailed -h|--help --format="
|
||||
[pkica ca template remove]="-n|--name= -h|--help"
|
||||
[pkica ca template update]="-f|--file= -d|--default-template= -h|--help"
|
||||
[pkica cert-registry db info]="-h|--help --format="
|
||||
[pkica cert-registry db new]="-s|--storage-type= -c|--connection-string= --command-timeout= -b|--begin-use-empty --force -h|--help"
|
||||
[pkica cert-registry db upgrade]="--command-timeout= -h|--help"
|
||||
[pkica cert-registry import-legacy]="-c|--connection-string= -t|--timeout= -h|--help"
|
||||
[pkica ra db info]="-h|--help --format="
|
||||
[pkica ra db new]="-s|--storage-type= -c|--connection-string= --command-timeout= -b|--begin-use-empty --force -h|--help"
|
||||
[pkica ra db upgrade]="--command-timeout= -h|--help"
|
||||
[pkica ra folder add]="-n|--name= -h|--help"
|
||||
[pkica ra folder delete]="-n|--name= -h|--help"
|
||||
[pkica ra folder get]="-n|--name= -h|--help --format="
|
||||
[pkica ra folder list]="-h|--help --format="
|
||||
[pkica ra folder rename]="-n|--name= --new-name= -h|--help"
|
||||
[pkica ra group add]="-n|--name= -h|--help"
|
||||
[pkica ra group add-operator]="-n|--name= -l|--login= -h|--help"
|
||||
[pkica ra group get]="-n|--name= -h|--help --format="
|
||||
[pkica ra group list]="-h|--help --format="
|
||||
[pkica ra group remove-operator]="-n|--name= -l|--login= -h|--help"
|
||||
[pkica ra group rename]="-n|--name= --new-name= -h|--help"
|
||||
[pkica ra import-legacy]="-c|--connection-string= -l|--login= --folder-id= --include-subfolders --skip-empty-users --skip-revoked-certs --skip-expired-certs -t|--timeout= -h|--help"
|
||||
[pkica ra operator activate]="--login= -h|--help"
|
||||
[pkica ra operator add]="-l|--last-name= -f|--first-name= -m|--middle-name= --tls-cert-file= --signing-cert-file= -g|--group= --default-folder= --login= -h|--help"
|
||||
[pkica ra operator deactivate]="--login= -h|--help"
|
||||
[pkica ra operator get]="-l|--login= -h|--help --format="
|
||||
[pkica ra operator list]="-g|--group= -l|--last-name= -f|--first-name= -m|--middle-name= -t|--take= -h|--help --format="
|
||||
[pkica ra operator update]="--login= --new-login= -l|--last-name= -f|--first-name= -m|--middle-name= --tls-cert-file= --signing-cert-file= --default-folder= --clear-default-folder -h|--help"
|
||||
[pkica ra permission get]="--folder= --group= -h|--help --format="
|
||||
[pkica ra permission grant]="--folder= --all-folders --group= --all-groups --manage-user --submit-cert-request --resolve-cert-request --submit-rev-request --resolve-rev-request --all -h|--help"
|
||||
[pkica ra permission revoke]="--folder= --all-folders --group= --all-groups --manage-user --submit-cert-request --resolve-cert-request --submit-rev-request --resolve-rev-request -h|--help"
|
||||
[pkica ra settings get]="-n|--name= -f|--file= -h|--help"
|
||||
[pkica ra settings set]="-n|--name= -v|--value= -d|--default -f|--file= -h|--help"
|
||||
)
|
||||
|
||||
# Если $2 - опция команды $1, принимающая значение, печатает ее длинное имя.
|
||||
_pkica_argopt()
|
||||
{
|
||||
local spec
|
||||
for spec in ${_pkica_opts[$1]:--h|--help}; do
|
||||
[[ $spec == *= ]] || continue
|
||||
spec=${spec%=}
|
||||
if [[ "|$spec|" == *"|$2|"* ]]; then
|
||||
printf '%s\n' "${spec##*|}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Дополнение значения опции $2 (длинное имя) команды $1.
|
||||
_pkica_values()
|
||||
{
|
||||
local path=$1 opt=$2 values=
|
||||
|
||||
case $opt in
|
||||
--format) values="Json Yaml" ;;
|
||||
--storage-type) values="PostgreSql SqlServer" ;;
|
||||
--sign-tool-class) values="KC1 KC2 KC3 KB1 KB2 KA1" ;;
|
||||
--key-spec) values="Signature KeyExchange" ;;
|
||||
--key-location | --store-location) values="CurrentUser LocalMachine" ;;
|
||||
--store-name) values="My Root CertificateAuthority AuthRoot AddressBook Disallowed TrustedPeople TrustedPublisher" ;;
|
||||
--serial-number-algorithm) values="CryptoPro Msca" ;;
|
||||
--provider-type) values="80 81" ;;
|
||||
--default-template) values="SubCa User Tls Ocsp Tsp Qual QualOcsp QualTsp" ;;
|
||||
--usage)
|
||||
if [[ $path == "pkica ca signing-key load" ]]; then
|
||||
values="SignCertAndCrl SignCrl"
|
||||
else
|
||||
values="SignCertAndCrl SignCrl Retired"
|
||||
fi
|
||||
;;
|
||||
--value)
|
||||
[[ $path == *" identification-kind" ]] &&
|
||||
values="Personal RemoteCert RemotePassport RemoteSystem"
|
||||
;;
|
||||
--name)
|
||||
[[ $path == "pkica ra settings "* ]] &&
|
||||
values="NamePolicy Templates DefaultAuthority"
|
||||
;;
|
||||
--validity | --key-validity)
|
||||
COMPREPLY=($(compgen -W "Years: Months: Weeks: Days: Hours: Minutes:" -- "$cur"))
|
||||
[[ ${COMPREPLY-} == *: ]] && compopt -o nospace 2>/dev/null
|
||||
declare -F __ltrim_colon_completions >/dev/null &&
|
||||
__ltrim_colon_completions "$cur"
|
||||
return
|
||||
;;
|
||||
--file | --out-file | --extensions-file | --tls-cert-file | --signing-cert-file)
|
||||
if declare -F _filedir >/dev/null; then
|
||||
_filedir
|
||||
else
|
||||
compopt -o filenames 2>/dev/null
|
||||
COMPREPLY=($(compgen -f -- "$cur"))
|
||||
fi
|
||||
return
|
||||
;;
|
||||
--output-folder)
|
||||
if declare -F _filedir >/dev/null; then
|
||||
_filedir -d
|
||||
else
|
||||
compopt -o filenames 2>/dev/null
|
||||
COMPREPLY=($(compgen -d -- "$cur"))
|
||||
fi
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
COMPREPLY=($(compgen -W "$values" -- "$cur"))
|
||||
}
|
||||
|
||||
_pkica()
|
||||
{
|
||||
local cur prev words cword split=false
|
||||
if declare -F _init_completion >/dev/null; then
|
||||
_init_completion -s -n : || return
|
||||
else
|
||||
words=("${COMP_WORDS[@]}")
|
||||
cword=$COMP_CWORD
|
||||
cur=${COMP_WORDS[COMP_CWORD]}
|
||||
prev=${COMP_WORDS[COMP_CWORD - 1]}
|
||||
fi
|
||||
|
||||
# Определяем путь команды, пропуская опции и их значения.
|
||||
local path=pkica i w skip=false
|
||||
for ((i = 1; i < cword; i++)); do
|
||||
w=${words[i]}
|
||||
if $skip; then
|
||||
skip=false
|
||||
elif [[ $w == -* ]]; then
|
||||
[[ $w != *=* ]] && _pkica_argopt "$path" "$w" >/dev/null && skip=true
|
||||
elif [[ " ${_pkica_cmds[$path]-} " == *" $w "* ]]; then
|
||||
path+=" $w"
|
||||
fi
|
||||
done
|
||||
|
||||
# Значение опции: "--opt value" или "--opt=value".
|
||||
local opt
|
||||
if $skip || $split; then
|
||||
if opt=$(_pkica_argopt "$path" "$prev"); then
|
||||
_pkica_values "$path" "$opt"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
local subcmds=${_pkica_cmds[$path]-}
|
||||
if [[ $cur != -* && -n $subcmds ]]; then
|
||||
COMPREPLY=($(compgen -W "$subcmds" -- "$cur"))
|
||||
return
|
||||
fi
|
||||
|
||||
# Опции, еще не указанные в командной строке. Без "-" в начале
|
||||
# предлагаются только длинные имена.
|
||||
local spec alias used list=() aliases
|
||||
for spec in ${_pkica_opts[$path]:--h|--help}; do
|
||||
spec=${spec%=}
|
||||
used=false
|
||||
IFS='|' read -ra aliases <<<"$spec"
|
||||
for ((i = 1; i < cword; i++)); do
|
||||
w=${words[i]%%=*}
|
||||
for alias in "${aliases[@]}"; do
|
||||
[[ $w == "$alias" ]] && used=true && break 2
|
||||
done
|
||||
done
|
||||
$used && continue
|
||||
if [[ $cur == -* ]]; then
|
||||
list+=("${aliases[@]}")
|
||||
else
|
||||
list+=("${aliases[${#aliases[@]} - 1]}")
|
||||
fi
|
||||
done
|
||||
COMPREPLY=($(compgen -W "${list[*]}" -- "$cur"))
|
||||
}
|
||||
|
||||
complete -F _pkica pkica
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
# Сверка таблиц pkica.bash-completion с реальным выводом `pkica ... --help`.
|
||||
# Запуск (из WSL/Linux): bash tests/check_tree.sh [путь к pkica]
|
||||
# Без аргумента используется pkica из PATH.
|
||||
|
||||
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
pkica=${1:-$(command -v pkica)}
|
||||
[[ -x $pkica ]] || { echo "pkica not found: укажите путь аргументом"; exit 1; }
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "$root/pkica.bash-completion" || exit 1
|
||||
|
||||
fail=0 nodes=0
|
||||
|
||||
walk()
|
||||
{
|
||||
local path=$1 help cmds opts expected_cmds expected_opts sub
|
||||
help=$("$pkica" ${path#pkica} --help 2>&1)
|
||||
nodes=$((nodes + 1))
|
||||
|
||||
cmds=$(awk '/^Commands:/{f=1;next} f&&/^$/{f=0} f&&/^ [a-z]/{print $1}' <<<"$help" |
|
||||
tr '\n' ' ' | sed 's/ $//')
|
||||
opts=$(awk '/^Options:/{f=1;next} /^(Commands:)?$/{f=0}
|
||||
f&&/^ -/{s=$1; gsub(/-\?\|/,"",s); if($2 ~ /^</) s=s "="; printf "%s ", s}' <<<"$help" |
|
||||
sed 's/ $//')
|
||||
|
||||
expected_cmds=${_pkica_cmds[$path]-}
|
||||
expected_opts=${_pkica_opts[$path]:--h|--help}
|
||||
|
||||
if [[ $cmds != "$expected_cmds" ]]; then
|
||||
fail=$((fail + 1))
|
||||
printf 'MISMATCH commands [%s]\n help: %s\n completion: %s\n' "$path" "$cmds" "$expected_cmds"
|
||||
fi
|
||||
if [[ $opts != "$expected_opts" ]]; then
|
||||
fail=$((fail + 1))
|
||||
printf 'MISMATCH options [%s]\n help: %s\n completion: %s\n' "$path" "$opts" "$expected_opts"
|
||||
fi
|
||||
|
||||
for sub in $cmds; do
|
||||
walk "$path $sub"
|
||||
done
|
||||
}
|
||||
|
||||
walk pkica
|
||||
|
||||
# Узлы в таблицах, которых нет в утилите.
|
||||
for key in "${!_pkica_cmds[@]}" "${!_pkica_opts[@]}"; do
|
||||
"$pkica" ${key#pkica} --help 2>&1 | grep -q "^Usage: $key " || {
|
||||
fail=$((fail + 1))
|
||||
printf 'UNKNOWN node in completion: [%s]\n' "$key"
|
||||
}
|
||||
done
|
||||
|
||||
echo "checked nodes: $nodes, mismatches: $fail"
|
||||
((fail == 0))
|
||||
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env bash
|
||||
# Модульные тесты pkica.bash-completion.
|
||||
# Запуск (из WSL/Linux): bash tests/test_completion.sh
|
||||
|
||||
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source /usr/share/bash-completion/bash_completion || exit 1
|
||||
# shellcheck disable=SC1091
|
||||
source "$root/pkica.bash-completion" || exit 1
|
||||
|
||||
pass=0 fail=0
|
||||
|
||||
# Разбивает строку на слова так же, как readline: по пробелам и символам
|
||||
# COMP_WORDBREAKS (группа подряд идущих разделителей - отдельное слово).
|
||||
_split_line()
|
||||
{
|
||||
local line=$1 c word= i
|
||||
COMP_WORDS=()
|
||||
for ((i = 0; i < ${#line}; i++)); do
|
||||
c=${line:i:1}
|
||||
if [[ $c == ' ' ]]; then
|
||||
[[ -n $word ]] && COMP_WORDS+=("$word")
|
||||
word=
|
||||
elif [[ $c == [=:] ]]; then
|
||||
if [[ -n $word && $word != *[=:] ]]; then
|
||||
COMP_WORDS+=("$word")
|
||||
word=
|
||||
fi
|
||||
word+=$c
|
||||
else
|
||||
if [[ $word == *[=:] ]]; then
|
||||
COMP_WORDS+=("$word")
|
||||
word=
|
||||
fi
|
||||
word+=$c
|
||||
fi
|
||||
done
|
||||
COMP_WORDS+=("$word")
|
||||
COMP_CWORD=$((${#COMP_WORDS[@]} - 1))
|
||||
}
|
||||
|
||||
# complete_line "строка" -> печатает отсортированные варианты через пробел
|
||||
complete_line()
|
||||
{
|
||||
COMP_LINE=$1
|
||||
COMP_POINT=${#COMP_LINE}
|
||||
_split_line "$COMP_LINE"
|
||||
COMPREPLY=()
|
||||
_pkica pkica "${COMP_WORDS[COMP_CWORD]}" "${COMP_WORDS[COMP_CWORD - 1]}"
|
||||
printf '%s\n' "${COMPREPLY[@]}" | sed '/^$/d' | sort | tr '\n' ' ' | sed 's/ $//'
|
||||
}
|
||||
|
||||
check()
|
||||
{
|
||||
local line=$1 expected got
|
||||
expected=$(printf '%s\n' $2 | sort | tr '\n' ' ' | sed 's/ $//')
|
||||
got=$(complete_line "$line")
|
||||
if [[ $got == "$expected" ]]; then
|
||||
pass=$((pass + 1))
|
||||
printf 'PASS %s\n' "$line|"
|
||||
else
|
||||
fail=$((fail + 1))
|
||||
printf 'FAIL %s\n expected: [%s]\n got: [%s]\n' "$line|" "$expected" "$got"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- команды ---
|
||||
check "pkica " "ca cert-registry ra"
|
||||
check "pkica c" "ca cert-registry"
|
||||
check "pkica -" "-v --version -h --help"
|
||||
check "pkica ca " "authority cert cert-request crl db import-legacy issue-cert new-cert-req new-root-cert rev-request signing-key template"
|
||||
check "pkica ca cert" "cert cert-request"
|
||||
check "pkica ca -" "-h --help"
|
||||
check "pkica ca crl schedule " "get update"
|
||||
check "pkica ca template encode-extension " "certificate-template identification-kind"
|
||||
check "pkica ra " "db folder group import-legacy operator permission settings"
|
||||
check "pkica cert-registry db " "info new upgrade"
|
||||
check "pkica ra group add" "add add-operator"
|
||||
|
||||
# --- опции ---
|
||||
check "pkica ca crl issue --" "--name --delta-only --next-update --final --key-index --output-folder --force --help"
|
||||
check "pkica ca crl issue " "--name --delta-only --next-update --final --key-index --output-folder --force --help"
|
||||
check "pkica ca authority show -" "-n --name -h --help --format"
|
||||
check "pkica ca authority set-default --n" "--name --none"
|
||||
check "pkica ra permission grant --all" "--all-folders --all-groups --all"
|
||||
check "pkica ra settings set -" "-n --name -v --value -d --default -f --file -h --help"
|
||||
|
||||
# уже указанные опции (и их синонимы) не предлагаются
|
||||
check "pkica ca authority show -n x -" "-h --help --format"
|
||||
check "pkica ca authority show --name x --format Json -" "-h --help"
|
||||
check "pkica ca authority show --name=x --" "--help --format"
|
||||
|
||||
# значения опций не мешают определению пути
|
||||
check "pkica ca crl issue -n ca1 -d -" "-u --next-update --final --key-index -o --output-folder --force -h --help"
|
||||
|
||||
# --- значения опций ---
|
||||
check "pkica ca authority show --format " "Json Yaml"
|
||||
check "pkica ca authority show --format J" "Json"
|
||||
check "pkica ca authority show --format=" "Json Yaml"
|
||||
check "pkica ca authority show --format=Y" "Yaml"
|
||||
check "pkica ca db new -s " "PostgreSql SqlServer"
|
||||
check "pkica ra db new --storage-type P" "PostgreSql"
|
||||
check "pkica ca new-cert-req --sign-tool-class K" "KC1 KC2 KC3 KB1 KB2 KA1"
|
||||
check "pkica ca new-cert-req -k " "Signature KeyExchange"
|
||||
check "pkica ca new-root-cert --key-location " "CurrentUser LocalMachine"
|
||||
check "pkica ca new-root-cert --serial-number-algorithm " "CryptoPro Msca"
|
||||
check "pkica ca new-root-cert -v " "Years: Months: Weeks: Days: Hours: Minutes:"
|
||||
check "pkica ca new-root-cert -V M" "Months: Minutes:"
|
||||
check "pkica ca signing-key add --store-location " "CurrentUser LocalMachine"
|
||||
check "pkica ca signing-key add -u " "SignCertAndCrl SignCrl Retired"
|
||||
check "pkica ca signing-key change-usage --usage S" "SignCertAndCrl SignCrl"
|
||||
check "pkica ca signing-key load -u " "SignCertAndCrl SignCrl"
|
||||
check "pkica ca template add -d " "SubCa User Tls Ocsp Tsp Qual QualOcsp QualTsp"
|
||||
check "pkica ca template update --default-template Q" "Qual QualOcsp QualTsp"
|
||||
check "pkica ca template encode-extension identification-kind -v " "Personal RemoteCert RemotePassport RemoteSystem"
|
||||
check "pkica ra settings get -n " "NamePolicy Templates DefaultAuthority"
|
||||
check "pkica ra settings set --name=T" "Templates"
|
||||
|
||||
# опции со свободным значением - без вариантов
|
||||
check "pkica ca authority show -n " ""
|
||||
check "pkica ca crl issue -u " ""
|
||||
check "pkica ca crl get -v " ""
|
||||
check "pkica ra settings set -v " ""
|
||||
check "pkica ra operator add -f " ""
|
||||
check "pkica ca new-cert-req -s " ""
|
||||
check "pkica ca new-cert-req -n " ""
|
||||
|
||||
# флаг с той же буквой, что и опция с аргументом в других командах
|
||||
check "pkica ca crl issue -d " "--name --next-update --final --key-index --output-folder --force --help"
|
||||
check "pkica ca template list -d " "--file --help --format"
|
||||
|
||||
# --- файлы и каталоги ---
|
||||
tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$tmp"' EXIT
|
||||
mkdir "$tmp/outdir"
|
||||
touch "$tmp/request.req" "$tmp/ext.bin"
|
||||
cd "$tmp" || exit 1
|
||||
check "pkica ca issue-cert -f r" "request.req"
|
||||
check "pkica ca issue-cert --file=r" "request.req"
|
||||
check "pkica ca issue-cert -o o" "outdir"
|
||||
check "pkica ca new-cert-req --extensions-file e" "ext.bin"
|
||||
check "pkica ra operator add --tls-cert-file e" "ext.bin"
|
||||
check "pkica ra settings set -f r" "request.req"
|
||||
check "pkica ca crl get -o " "outdir"
|
||||
check "pkica ca crl issue --output-folder " "outdir"
|
||||
|
||||
echo
|
||||
echo "passed: $pass, failed: $fail"
|
||||
((fail == 0))
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env bash
|
||||
# Интерактивные тесты pkica.bash-completion: реальный bash + readline в tmux,
|
||||
# нажатие TAB и проверка содержимого строки ввода.
|
||||
# Запуск (из WSL/Linux, нужен tmux): bash tests/test_interactive.sh
|
||||
|
||||
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
command -v tmux >/dev/null || { echo "tmux not found"; exit 1; }
|
||||
|
||||
sess=pkica-compl-$$
|
||||
tmp=$(mktemp -d)
|
||||
trap 'tmux kill-session -t "$sess" 2>/dev/null; rm -rf "$tmp"' EXIT
|
||||
|
||||
mkdir "$tmp/outdir"
|
||||
touch "$tmp/request.req"
|
||||
cat >"$tmp/rc" <<EOF
|
||||
source /usr/share/bash-completion/bash_completion
|
||||
source "$root/pkica.bash-completion"
|
||||
bind 'set bell-style none'
|
||||
PS1='$ '
|
||||
cd "$tmp"
|
||||
EOF
|
||||
|
||||
tmux new-session -d -s "$sess" -x 200 -y 50 "bash --rcfile '$tmp/rc' -i"
|
||||
sleep 1
|
||||
|
||||
pass=0 fail=0
|
||||
|
||||
# check "ввод" "ожидаемая строка после TAB" (после TAB вводится маркер "#",
|
||||
# т.к. capture-pane обрезает концевые пробелы)
|
||||
check()
|
||||
{
|
||||
local input=$1 expected="$ $2#" got
|
||||
tmux send-keys -t "$sess" C-u "$input" Tab "#"
|
||||
sleep 0.5
|
||||
got=$(tmux capture-pane -p -t "$sess" | sed '/^$/d' | tail -1)
|
||||
if [[ $got == "$expected" ]]; then
|
||||
pass=$((pass + 1))
|
||||
printf 'PASS %s<TAB>\n' "$input"
|
||||
else
|
||||
fail=$((fail + 1))
|
||||
printf 'FAIL %s<TAB>\n expected: [%s]\n got: [%s]\n' "$input" "$expected" "$got"
|
||||
fi
|
||||
tmux send-keys -t "$sess" C-u
|
||||
tmux send-keys -t "$sess" C-l
|
||||
sleep 0.2
|
||||
}
|
||||
|
||||
check "pkica ce" "pkica cert-registry "
|
||||
check "pkica ca sig" "pkica ca signing-key "
|
||||
check "pkica ca signing-key cha" "pkica ca signing-key change-usage "
|
||||
check "pkica ca authority show --fo" "pkica ca authority show --format "
|
||||
check "pkica ca authority show --format Y" "pkica ca authority show --format Yaml "
|
||||
check "pkica ca authority show --format=J" "pkica ca authority show --format=Json "
|
||||
check "pkica ca new-root-cert -v Y" "pkica ca new-root-cert -v Years:"
|
||||
check "pkica ca template add -d Su" "pkica ca template add -d SubCa "
|
||||
check "pkica ca issue-cert -f " "pkica ca issue-cert -f "
|
||||
check "pkica ca issue-cert -f req" "pkica ca issue-cert -f request.req "
|
||||
check "pkica ca crl get -o " "pkica ca crl get -o outdir/"
|
||||
check "./pkica ra fo" "./pkica ra folder "
|
||||
|
||||
# Пустое слово: при TAB TAB в списке должны быть и файлы, и каталоги.
|
||||
tmux send-keys -t "$sess" C-u "pkica ca issue-cert -f " Tab Tab
|
||||
sleep 0.5
|
||||
screen=$(tmux capture-pane -p -t "$sess")
|
||||
if [[ $screen == *request.req* && $screen == *outdir/* ]]; then
|
||||
pass=$((pass + 1))
|
||||
echo "PASS pkica ca issue-cert -f <TAB><TAB> lists files"
|
||||
else
|
||||
fail=$((fail + 1))
|
||||
printf 'FAIL pkica ca issue-cert -f <TAB><TAB> lists files\n%s\n' "$screen"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "passed: $pass, failed: $fail"
|
||||
((fail == 0))
|
||||
Reference in New Issue
Block a user