Gpg-based authorization #3
@@ -3057,6 +3057,17 @@
|
||||
"gpg.error.no_gpg_keys_found": "V databázi nebyl nalezen žádný známý klíč pro tento podpis",
|
||||
"gpg.error.not_signed_commit": "Nepodepsaná revize",
|
||||
"gpg.error.failed_retrieval_gpg_keys": "Nelze získat žádný klíč propojený s účtem přispěvatele",
|
||||
"gpg.signin.title": "Přihlásit se",
|
||||
"gpg.signin.nonce_label": "Podepište tento nonce svým soukromým klíčem",
|
||||
"gpg.signin.command_hint": "Spusťte tento příkaz a vložte výstup níže:",
|
||||
"gpg.signin.copy": "Kopírovat",
|
||||
"gpg.signin.signed_output": "Podepsaný výstup",
|
||||
"gpg.signin.submit": "Přihlásit se",
|
||||
"gpg.signup.title": "Vytvořit účet",
|
||||
"gpg.signup.paste_key": "Vložte svůj veřejný GPG klíč",
|
||||
"gpg.signup.proceed": "Pokračovat",
|
||||
"gpg.signup.submit": "Ověřit a vytvořit účet",
|
||||
"gpg.signup.already_have_account": "Již máte účet?",
|
||||
"units.unit": "Jednotka",
|
||||
"units.error.no_unit_allowed_repo": "Nejste oprávněni přistupovat k žádné části tohoto repozitáře.",
|
||||
"units.error.unit_not_allowed": "Nejste oprávněni přistupovat k této části repozitáře.",
|
||||
@@ -3307,4 +3318,4 @@
|
||||
"git.filemode.executable_file": "Spustitelný soubor",
|
||||
"git.filemode.symbolic_link": "Symbolický odkaz",
|
||||
"git.filemode.submodule": "Submodul"
|
||||
}
|
||||
}
|
||||
@@ -3005,6 +3005,17 @@
|
||||
"gpg.error.no_gpg_keys_found": "Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden",
|
||||
"gpg.error.not_signed_commit": "Kein signierter Commit",
|
||||
"gpg.error.failed_retrieval_gpg_keys": "Fehler beim Abrufen eines Keys des Commiter-Kontos",
|
||||
"gpg.signin.title": "Anmelden",
|
||||
"gpg.signin.nonce_label": "Signiere diesen Nonce mit deinem privaten Schlüssel",
|
||||
"gpg.signin.command_hint": "Führe diesen Befehl aus und füge die Ausgabe unten ein:",
|
||||
"gpg.signin.copy": "Kopieren",
|
||||
"gpg.signin.signed_output": "Signierte Ausgabe",
|
||||
"gpg.signin.submit": "Anmelden",
|
||||
"gpg.signup.title": "Konto erstellen",
|
||||
"gpg.signup.paste_key": "Füge deinen öffentlichen GPG-Schlüssel ein",
|
||||
"gpg.signup.proceed": "Weiter",
|
||||
"gpg.signup.submit": "Verifizieren & Konto erstellen",
|
||||
"gpg.signup.already_have_account": "Bereits ein Konto?",
|
||||
"units.unit": "Einheit",
|
||||
"units.error.no_unit_allowed_repo": "Du hast keine Berechtigung, auf etwas in diesem Repository zuzugreifen.",
|
||||
"units.error.unit_not_allowed": "Du hast keine Berechtigung, um auf diesen Repository-Bereich zuzugreifen.",
|
||||
@@ -3249,4 +3260,4 @@
|
||||
"git.filemode.executable_file": "Ausführbare Datei",
|
||||
"git.filemode.symbolic_link": "Softlink",
|
||||
"git.filemode.submodule": "Submodul"
|
||||
}
|
||||
}
|
||||
@@ -3503,6 +3503,17 @@
|
||||
"gpg.error.failed_retrieval_gpg_keys": "Failed to retrieve any key attached to the committer's account",
|
||||
"gpg.error.probable_bad_signature": "WARNING! Although there is a key with this ID in the database, it does not verify this commit! This commit is SUSPICIOUS.",
|
||||
"gpg.error.probable_bad_default_signature": "WARNING! Although the default key has this ID, it does not verify this commit! This commit is SUSPICIOUS.",
|
||||
"gpg.signin.title": "Sign In",
|
||||
"gpg.signin.nonce_label": "Sign this nonce with your private key",
|
||||
"gpg.signin.command_hint": "Run this command, then paste the output below:",
|
||||
"gpg.signin.copy": "Copy",
|
||||
"gpg.signin.signed_output": "Signed output",
|
||||
"gpg.signin.submit": "Sign In",
|
||||
"gpg.signup.title": "Create your account",
|
||||
"gpg.signup.paste_key": "Paste your GPG public key",
|
||||
"gpg.signup.proceed": "Proceed",
|
||||
"gpg.signup.submit": "Verify & Create Account",
|
||||
"gpg.signup.already_have_account": "Already have an account?",
|
||||
"units.unit": "Unit",
|
||||
"units.error.no_unit_allowed_repo": "You are not allowed to access any section of this repository.",
|
||||
"units.error.unit_not_allowed": "You are not allowed to access this repository section.",
|
||||
@@ -3888,4 +3899,4 @@
|
||||
"actions.general.cross_repo_selected": "Selected repositories",
|
||||
"actions.general.cross_repo_target_repos": "Target Repositories",
|
||||
"actions.general.cross_repo_add": "Add Target Repository"
|
||||
}
|
||||
}
|
||||
@@ -2732,6 +2732,17 @@
|
||||
"gpg.error.no_gpg_keys_found": "No se encontró ninguna clave conocida en la base de datos para esta firma",
|
||||
"gpg.error.not_signed_commit": "No es un commit firmado",
|
||||
"gpg.error.failed_retrieval_gpg_keys": "No se pudo recuperar cualquier clave adjunta a la cuenta del committer",
|
||||
"gpg.signin.title": "Iniciar sesión",
|
||||
"gpg.signin.nonce_label": "Firma este nonce con tu clave privada",
|
||||
"gpg.signin.command_hint": "Ejecuta este comando y pega el resultado abajo:",
|
||||
"gpg.signin.copy": "Copiar",
|
||||
"gpg.signin.signed_output": "Salida firmada",
|
||||
"gpg.signin.submit": "Iniciar sesión",
|
||||
"gpg.signup.title": "Crear tu cuenta",
|
||||
"gpg.signup.paste_key": "Pega tu clave pública GPG",
|
||||
"gpg.signup.proceed": "Continuar",
|
||||
"gpg.signup.submit": "Verificar y crear cuenta",
|
||||
"gpg.signup.already_have_account": "¿Ya tienes una cuenta?",
|
||||
"units.unit": "Unidad",
|
||||
"units.error.no_unit_allowed_repo": "No tiene permisos para acceder a ninguna sección de este repositorio.",
|
||||
"units.error.unit_not_allowed": "No tiene permisos para acceder a esta sección del repositorio.",
|
||||
@@ -2960,4 +2971,4 @@
|
||||
"git.filemode.executable_file": "Archivo ejecutable",
|
||||
"git.filemode.symbolic_link": "Enlace simbólico",
|
||||
"git.filemode.submodule": "Submódulo"
|
||||
}
|
||||
}
|
||||
@@ -3498,6 +3498,17 @@
|
||||
"gpg.error.failed_retrieval_gpg_keys": "Impossible de récupérer la clé liée au compte de l'auteur",
|
||||
"gpg.error.probable_bad_signature": "AVERTISSEMENT ! Bien qu’il y ait une clé avec cet ID dans la base de données, elle ne vérifie pas cette révision ! Cette révision est SUSPECTE.",
|
||||
"gpg.error.probable_bad_default_signature": "AVERTISSEMENT ! Bien que la clé par défaut ait cet ID, elle ne vérifie pas cette révision ! Cette révision est SUSPECTE.",
|
||||
"gpg.signin.title": "Se connecter",
|
||||
"gpg.signin.nonce_label": "Signez ce nonce avec votre clé privée",
|
||||
"gpg.signin.command_hint": "Exécutez cette commande puis collez le résultat ci-dessous :",
|
||||
"gpg.signin.copy": "Copier",
|
||||
"gpg.signin.signed_output": "Sortie signée",
|
||||
"gpg.signin.submit": "Se connecter",
|
||||
"gpg.signup.title": "Créer votre compte",
|
||||
"gpg.signup.paste_key": "Collez votre clé publique GPG",
|
||||
"gpg.signup.proceed": "Continuer",
|
||||
"gpg.signup.submit": "Vérifier & créer le compte",
|
||||
"gpg.signup.already_have_account": "Vous avez déjà un compte ?",
|
||||
"units.unit": "Ressource",
|
||||
"units.error.no_unit_allowed_repo": "Vous n'êtes pas autorisé à accéder à n'importe quelle section de ce dépôt.",
|
||||
"units.error.unit_not_allowed": "Vous n'êtes pas autorisé à accéder à cette section du dépôt.",
|
||||
@@ -3866,4 +3877,4 @@
|
||||
"actions.general.cross_repo_selected": "Dépôts sélectionnés",
|
||||
"actions.general.cross_repo_target_repos": "Dépôts cibles",
|
||||
"actions.general.cross_repo_add": "Ajouter un dépôt cible"
|
||||
}
|
||||
}
|
||||
@@ -3499,6 +3499,17 @@
|
||||
"gpg.error.failed_retrieval_gpg_keys": "커미터 계정에 연결된 키를 가져오지 못함",
|
||||
"gpg.error.probable_bad_signature": "경고! 이 ID를 가진 키가 데이터베이스에 있지만 이 커밋을 검증하지 않습니다! 이 커밋은 의심스럽습니다.",
|
||||
"gpg.error.probable_bad_default_signature": "경고! 기본 키는 이 ID를 가지지만 이 커밋을 검증하지 않습니다! 이 커밋은 의심스럽습니다.",
|
||||
"gpg.signin.title": "로그인",
|
||||
"gpg.signin.nonce_label": "개인 키로 이 nonce에 서명하세요",
|
||||
"gpg.signin.command_hint": "이 명령을 실행하고 아래에 출력을 붙여넣으세요:",
|
||||
"gpg.signin.copy": "복사",
|
||||
"gpg.signin.signed_output": "서명된 출력",
|
||||
"gpg.signin.submit": "로그인",
|
||||
"gpg.signup.title": "계정 만들기",
|
||||
"gpg.signup.paste_key": "GPG 공개 키를 붙여넣으세요",
|
||||
"gpg.signup.proceed": "계속",
|
||||
"gpg.signup.submit": "확인 및 계정 생성",
|
||||
"gpg.signup.already_have_account": "이미 계정이 있으신가요?",
|
||||
"units.unit": "단위",
|
||||
"units.error.no_unit_allowed_repo": "이 리포지토리의 어떤 섹션에도 접근할 수 없습니다.",
|
||||
"units.error.unit_not_allowed": "이 리포지토리 섹션에 접근할 수 없습니다.",
|
||||
@@ -3879,4 +3890,4 @@
|
||||
"actions.general.cross_repo_selected": "선택된 리포지토리",
|
||||
"actions.general.cross_repo_target_repos": "대상 리포지토리",
|
||||
"actions.general.cross_repo_add": "대상 리포지토리 추가"
|
||||
}
|
||||
}
|
||||
@@ -2755,6 +2755,17 @@
|
||||
"gpg.error.no_gpg_keys_found": "Не найден ключ, соответствующий данной подписи",
|
||||
"gpg.error.not_signed_commit": "Неподписанный коммит",
|
||||
"gpg.error.failed_retrieval_gpg_keys": "Не удалось получить ни одного ключа GPG автора коммита",
|
||||
"gpg.signin.title": "Войти",
|
||||
"gpg.signin.nonce_label": "Подпишите этот nonce своим приватным ключом",
|
||||
"gpg.signin.command_hint": "Выполните команду и вставьте результат ниже:",
|
||||
"gpg.signin.copy": "Копировать",
|
||||
"gpg.signin.signed_output": "Подписанный вывод",
|
||||
"gpg.signin.submit": "Войти",
|
||||
"gpg.signup.title": "Создать аккаунт",
|
||||
"gpg.signup.paste_key": "Вставьте ваш публичный GPG-ключ",
|
||||
"gpg.signup.proceed": "Продолжить",
|
||||
"gpg.signup.submit": "Подтвердить и создать аккаунт",
|
||||
"gpg.signup.already_have_account": "Уже есть аккаунт?",
|
||||
"units.unit": "Элемент",
|
||||
"units.error.no_unit_allowed_repo": "У вас нет доступа ни к одному разделу этого репозитория.",
|
||||
"units.error.unit_not_allowed": "У вас нет доступа к этому разделу репозитория.",
|
||||
@@ -2983,4 +2994,4 @@
|
||||
"git.filemode.executable_file": "Исполняемый файл",
|
||||
"git.filemode.symbolic_link": "Символическая ссылка",
|
||||
"git.filemode.submodule": "Подмодуль"
|
||||
}
|
||||
}
|
||||
@@ -3498,6 +3498,17 @@
|
||||
"gpg.error.failed_retrieval_gpg_keys": "找不到任何与该提交者账号相关的密钥",
|
||||
"gpg.error.probable_bad_signature": "警告!虽然数据库中有一个此 ID 的密钥,但它没有验证此提交!此提交是可疑的。",
|
||||
"gpg.error.probable_bad_default_signature": "警告!虽然默认密钥拥有此 ID,但它没有验证此提交!此提交是可疑的。",
|
||||
"gpg.signin.title": "登录",
|
||||
"gpg.signin.nonce_label": "使用您的私钥签署此随机数",
|
||||
"gpg.signin.command_hint": "运行此命令,然后将输出粘贴到下方:",
|
||||
"gpg.signin.copy": "复制",
|
||||
"gpg.signin.signed_output": "签名输出",
|
||||
"gpg.signin.submit": "登录",
|
||||
"gpg.signup.title": "创建账户",
|
||||
"gpg.signup.paste_key": "粘贴您的 GPG 公钥",
|
||||
"gpg.signup.proceed": "继续",
|
||||
"gpg.signup.submit": "验证并创建账户",
|
||||
"gpg.signup.already_have_account": "已有账户?",
|
||||
"units.unit": "单元",
|
||||
"units.error.no_unit_allowed_repo": "您没有被允许访问此仓库的任何单元。",
|
||||
"units.error.unit_not_allowed": "您没有权限访问此仓库单元",
|
||||
@@ -3866,4 +3877,4 @@
|
||||
"actions.general.cross_repo_selected": "选择的仓库",
|
||||
"actions.general.cross_repo_target_repos": "目标仓库",
|
||||
"actions.general.cross_repo_add": "添加目标仓库"
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,7 @@
|
||||
{{end}}
|
||||
{{if $.RenderedDescription}}
|
||||
<li>
|
||||
{{svg "octicon-info"}}
|
||||
<div class="render-content markup">{{$.RenderedDescription}}</div>
|
||||
</li>
|
||||
{{end}}
|
||||
@@ -79,6 +80,16 @@
|
||||
</li>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if .ContextUser.GPGKeys}}
|
||||
{{if .GPGKeys}}
|
||||
<li>
|
||||
{{svg "octicon-key"}}
|
||||
<a class="tw-font-mono tw-text-sm" href="{{AppSubUrl}}/{{$.ContextUser.Name}}.gpg" target="_blank">
|
||||
GPG: {{(index .GPGKeys 0).PaddedKeyID}}{{if gt (len .GPGKeys) 1}} +{{.GPGKeysExtra}}{{end}}
|
||||
</a>
|
||||
</li>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<li>{{svg "octicon-calendar"}} <span>{{ctx.Locale.Tr "user.joined_on" (DateUtils.AbsoluteShort .ContextUser.CreatedUnix)}}</span></li>
|
||||
{{if and .Orgs .HasOrgsVisible}}
|
||||
<li>
|
||||
|
||||
@@ -1,76 +1,45 @@
|
||||
<div class="ui container fluid">
|
||||
{{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn)}}
|
||||
{{template "base/alert" .}}
|
||||
{{end}}
|
||||
<h4 class="ui top attached header center">
|
||||
{{if .LinkAccountMode}}
|
||||
{{ctx.Locale.Tr "auth.oauth_signin_title"}}
|
||||
{{else}}
|
||||
{{ctx.Locale.Tr "auth.login_userpass"}}
|
||||
{{end}}
|
||||
{{ctx.Locale.Tr "gpg.signin.nonce_label"}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
{{if .EnablePasswordSignInForm}}
|
||||
<form class="ui form" action="{{.SignInLink}}" method="post">
|
||||
<div class="required field {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}">
|
||||
<label for="user_name">{{ctx.Locale.Tr "home.uname_holder"}}</label>
|
||||
<input id="user_name" type="text" name="user_name" value="{{.user_name}}" autofocus required tabindex="1">
|
||||
</div>
|
||||
{{if or (not .DisablePassword) .LinkAccountMode}}
|
||||
<div class="required field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}">
|
||||
<div class="tw-flex tw-mb-1">
|
||||
<label for="password" class="tw-flex-1">{{ctx.Locale.Tr "password"}}</label>
|
||||
<a href="{{AppSubUrl}}/user/forgot_password" tabindex="4">{{ctx.Locale.Tr "auth.forgot_password"}}</a>
|
||||
</div>
|
||||
<input id="password" name="password" type="password" value="{{.password}}" autocomplete="current-password" required tabindex="2">
|
||||
</div>
|
||||
{{end}}
|
||||
{{if not .LinkAccountMode}}
|
||||
<div class="inline field">
|
||||
<div class="ui checkbox">
|
||||
<label>{{ctx.Locale.Tr "auth.remember_me"}}</label>
|
||||
<input name="remember" type="checkbox" tabindex="5">
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{template "user/auth/captcha" .}}
|
||||
<form class="ui form" action="{{.SignInLink}}" method="post" id="signin-form">
|
||||
{{.CsrfTokenHtml}}
|
||||
{{template "base/alert" .}}
|
||||
<input type="hidden" id="hidden-nonce" name="nonce">
|
||||
|
||||
<div class="field">
|
||||
<button class="ui primary button tw-w-full" tabindex="3">
|
||||
{{if .LinkAccountMode}}
|
||||
{{ctx.Locale.Tr "auth.oauth_signin_submit"}}
|
||||
{{else}}
|
||||
{{ctx.Locale.Tr "sign_in"}}
|
||||
{{end}}
|
||||
</button>
|
||||
<label>{{ctx.Locale.Tr "gpg.signin.nonce_label"}}</label>
|
||||
<input id="token-field" type="text" readonly style="font-family: monospace;">
|
||||
</div>
|
||||
<div class="ui info message">
|
||||
<p>{{ctx.Locale.Tr "gpg.signin.command_hint"}}</p>
|
||||
<div style="display: flex; gap: 6px;">
|
||||
<input id="sign-command" type="text" readonly
|
||||
style="font-family: monospace; min-width: 0; flex: 1;">
|
||||
<button class="ui button" type="button" id="btn-copy-cmd">{{ctx.Locale.Tr "gpg.signin.copy"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="required field {{if .Err_GPGSign}}error{{end}}">
|
||||
<label for="gpg_signature">{{ctx.Locale.Tr "gpg.signin.signed_output"}}</label>
|
||||
<textarea id="gpg_signature" name="gpg_signature" rows="7"
|
||||
placeholder="-----BEGIN PGP SIGNED MESSAGE-----" style="font-family: monospace;"
|
||||
required>{{.gpg_signature}}</textarea>
|
||||
</div>
|
||||
<div class="inline field">
|
||||
<button class="ui primary button tw-w-full" type="submit">{{ctx.Locale.Tr "gpg.signin.submit"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
{{end}}{{/*end if .EnablePasswordSignInForm*/}}
|
||||
{{$showExternalAuthMethods := or .OAuth2Providers .EnableOpenIDSignIn .EnableSSPI}}
|
||||
{{if and $showExternalAuthMethods .EnablePasswordSignInForm}}
|
||||
<div class="divider divider-text">{{ctx.Locale.Tr "sign_in_or"}}</div>
|
||||
{{end}}
|
||||
{{if $showExternalAuthMethods}}
|
||||
{{template "user/auth/external_auth_methods" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if or .EnablePasskeyAuth .ShowRegistrationButton}}
|
||||
{{if .ShowRegistrationButton}}
|
||||
<div class="ui container fluid">
|
||||
<div class="ui attached segment header top tw-max-w-2xl tw-m-auto tw-flex tw-flex-col tw-items-center">
|
||||
{{if .EnablePasskeyAuth}}
|
||||
{{template "user/auth/webauthn_error" .}}
|
||||
<a class="signin-passkey">{{ctx.Locale.Tr "auth.signin_passkey"}}</a>
|
||||
{{end}}
|
||||
|
||||
{{if .ShowRegistrationButton}}
|
||||
<div class="field">
|
||||
<span>{{ctx.Locale.Tr "auth.need_account"}}</span>
|
||||
<a href="{{AppSubUrl}}/user/sign_up">{{ctx.Locale.Tr "auth.sign_up_now"}}</a>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="ui attached segment header top tw-flex tw-flex-col tw-items-center">
|
||||
<div class="field">
|
||||
<span>{{ctx.Locale.Tr "auth.need_account"}}</span>
|
||||
<a href="{{AppSubUrl}}/user/sign_up">{{ctx.Locale.Tr "auth.sign_up_now"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -1,70 +1,54 @@
|
||||
<div class="ui container fluid{{if .LinkAccountMode}} icon{{end}}">
|
||||
<div class="ui container fluid">
|
||||
<h4 class="ui top attached header center">
|
||||
{{if .LinkAccountMode}}
|
||||
{{ctx.Locale.Tr "auth.oauth_signup_title"}}
|
||||
{{else}}
|
||||
{{ctx.Locale.Tr "sign_up"}}
|
||||
{{end}}
|
||||
{{ctx.Locale.Tr "gpg.signup.title"}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
{{if .IsFirstTimeRegistration}}
|
||||
<p>{{ctx.Locale.Tr "auth.sign_up_tip"}}</p>
|
||||
{{end}}
|
||||
<form class="ui form" action="{{.SignUpLink}}" method="post">
|
||||
{{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister)}}
|
||||
{{template "base/alert" .}}
|
||||
{{end}}
|
||||
{{if .DisableRegistration}}
|
||||
<p>{{ctx.Locale.Tr "auth.disable_register_prompt"}}</p>
|
||||
{{else}}
|
||||
<div class="required field {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}">
|
||||
<label for="user_name">{{ctx.Locale.Tr "username"}}</label>
|
||||
<input id="user_name" type="text" name="user_name" value="{{.user_name}}" autofocus required>
|
||||
<form class="ui form" action="{{.SignUpLink}}" method="post" id="signup-form">
|
||||
{{.CsrfTokenHtml}}
|
||||
<input type="hidden" id="hidden-gpg-key" name="gpg_key">
|
||||
<input type="hidden" id="hidden-nonce" name="nonce">
|
||||
|
||||
<div id="step-key">
|
||||
{{template "base/alert" .}}
|
||||
<div class="required field {{if .Err_GPGKey}}error{{end}}">
|
||||
<label>{{ctx.Locale.Tr "gpg.signup.paste_key"}}</label>
|
||||
<textarea id="gpg_key" rows="7" placeholder="-----BEGIN PGP PUBLIC KEY BLOCK-----" style="font-family: monospace;" required>{{.gpg_key}}</textarea>
|
||||
</div>
|
||||
<div class="required field {{if .Err_Email}}error{{end}}">
|
||||
<label for="email">{{ctx.Locale.Tr "email"}}</label>
|
||||
<input id="email" name="email" type="email" value="{{.email}}" required>
|
||||
</div>
|
||||
|
||||
{{if not .DisablePassword}}
|
||||
<div class="required field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}">
|
||||
<label for="password">{{ctx.Locale.Tr "password"}}</label>
|
||||
<input id="password" name="password" type="password" value="{{.password}}" autocomplete="new-password" required>
|
||||
</div>
|
||||
<div class="required field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}">
|
||||
<label for="retype">{{ctx.Locale.Tr "re_type"}}</label>
|
||||
<input id="retype" name="retype" type="password" value="{{.retype}}" autocomplete="new-password" required>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{template "user/auth/captcha" .}}
|
||||
|
||||
<div class="inline field">
|
||||
<button class="ui primary button tw-w-full">
|
||||
{{if .LinkAccountMode}}
|
||||
{{ctx.Locale.Tr "auth.oauth_signup_submit"}}
|
||||
{{else}}
|
||||
{{ctx.Locale.Tr "auth.create_new_account"}}
|
||||
{{end}}
|
||||
</button>
|
||||
<button class="ui primary button tw-w-full" type="button" id="btn-proceed">{{ctx.Locale.Tr "gpg.signup.proceed"}}</button>
|
||||
</div>
|
||||
{{end}}
|
||||
{{$showExternalAuthMethods := or .OAuth2Providers .EnableOpenIDSignIn .EnableSSPI}}
|
||||
{{if $showExternalAuthMethods}}
|
||||
<div class="divider divider-text">{{ctx.Locale.Tr "sign_in_or"}}</div>
|
||||
{{template "user/auth/external_auth_methods" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<div id="step-sign" style="display: none;">
|
||||
{{template "base/alert" .}}
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "gpg.signin.nonce"}}</label>
|
||||
<input id="token-field" type="text" readonly style="font-family: monospace;">
|
||||
</div>
|
||||
<div class="ui info message">
|
||||
<p>{{ctx.Locale.Tr "gpg.signin.command_hint"}}</p>
|
||||
<div style="display: flex; gap: 6px;">
|
||||
<input id="sign-command" type="text" readonly style="font-family: monospace; min-width: 0; flex: 1;">
|
||||
<button class="ui button" type="button" id="btn-copy-cmd">{{ctx.Locale.Tr "gpg.signin.copy"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="required field {{if .Err_GPGSign}}error{{end}}">
|
||||
<label for="gpg_signature">{{ctx.Locale.Tr "gpg.signin.signed_output"}}</label>
|
||||
<textarea id="gpg_signature" name="gpg_signature" rows="7" placeholder="-----BEGIN PGP SIGNED MESSAGE-----" style="font-family: monospace;" required>{{.gpg_signature}}</textarea>
|
||||
</div>
|
||||
<div class="inline field">
|
||||
<button class="ui primary button tw-w-full" type="submit">{{ctx.Locale.Tr "gpg.signup.submit"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui container fluid">
|
||||
{{if not .LinkAccountMode}}
|
||||
<div class="ui attached segment header top tw-flex tw-flex-col tw-items-center">
|
||||
<div class="field">
|
||||
<span>{{ctx.Locale.Tr "auth.already_have_account"}}</span>
|
||||
<a href="{{AppSubUrl}}/user/login">{{ctx.Locale.Tr "auth.sign_in_now"}}</a>
|
||||
<span>{{ctx.Locale.Tr "gpg.signup.already_have_account"}}</span>
|
||||
<a href="{{AppSubUrl}}/user/login">{{ctx.Locale.Tr "gpg.signin.title"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,31 @@
|
||||
export function initGpgSignin() {
|
||||
const tokenField = document.getElementById('token-field') as HTMLInputElement;
|
||||
if (!tokenField) return;
|
||||
|
||||
const nonce = (() => {
|
||||
const arr = new Uint8Array(32);
|
||||
crypto.getRandomValues(arr);
|
||||
return Array.from(arr, (b) => b.toString(16).padStart(2, '0')).join('');
|
||||
})();
|
||||
|
||||
tokenField.value = nonce;
|
||||
(document.getElementById('hidden-nonce') as HTMLInputElement).value = nonce;
|
||||
(document.getElementById('sign-command') as HTMLInputElement).value =
|
||||
`echo "${nonce}" | gpg -a --detach-sig`;
|
||||
|
||||
document.getElementById('btn-copy-cmd')?.addEventListener('click', () => {
|
||||
navigator.clipboard.writeText(
|
||||
(document.getElementById('sign-command') as HTMLInputElement).value
|
||||
);
|
||||
});
|
||||
|
||||
document.getElementById('signin-form')?.addEventListener('submit', (e) => {
|
||||
const sig = (document.getElementById('gpg_signature') as HTMLTextAreaElement).value.trim();
|
||||
if (!sig ||
|
||||
(!sig.startsWith('-----BEGIN PGP SIGNED MESSAGE-----') &&
|
||||
!sig.startsWith('-----BEGIN PGP SIGNATURE-----'))) {
|
||||
e.preventDefault();
|
||||
alert('Paste the GPG signed output.');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
export function initGpgSignup() {
|
||||
const btnProceed = document.getElementById('btn-proceed');
|
||||
if (!btnProceed) return;
|
||||
|
||||
const nonce = (() => {
|
||||
const arr = new Uint8Array(32);
|
||||
crypto.getRandomValues(arr);
|
||||
return Array.from(arr, (b) => b.toString(16).padStart(2, '0')).join('');
|
||||
})();
|
||||
|
||||
btnProceed.addEventListener('click', () => {
|
||||
const key = (document.getElementById('gpg_key') as HTMLTextAreaElement).value.trim();
|
||||
if (!key || !key.startsWith('-----BEGIN PGP PUBLIC KEY BLOCK-----')) {
|
||||
alert('Paste a valid armored GPG public key.');
|
||||
return;
|
||||
}
|
||||
(document.getElementById('token-field') as HTMLInputElement).value = nonce;
|
||||
(document.getElementById('hidden-nonce') as HTMLInputElement).value = nonce;
|
||||
(document.getElementById('hidden-gpg-key') as HTMLInputElement).value = key;
|
||||
(document.getElementById('sign-command') as HTMLInputElement).value =
|
||||
`echo "${nonce}" | gpg -a --detach-sig`;
|
||||
document.getElementById('step-key')!.style.display = 'none';
|
||||
document.getElementById('step-sign')!.style.display = 'block';
|
||||
});
|
||||
|
||||
document.getElementById('signup-form')?.addEventListener('submit', (e) => {
|
||||
const sig = (document.getElementById('gpg_signature') as HTMLTextAreaElement).value.trim();
|
||||
if (!sig ||
|
||||
(!sig.startsWith('-----BEGIN PGP SIGNED MESSAGE-----') &&
|
||||
!sig.startsWith('-----BEGIN PGP SIGNATURE-----'))) {
|
||||
e.preventDefault();
|
||||
alert('Paste the GPG signed output.');
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('btn-copy-cmd')?.addEventListener('click', () => {
|
||||
const cmd = (document.getElementById('sign-command') as HTMLInputElement).value;
|
||||
navigator.clipboard.writeText(cmd);
|
||||
});
|
||||
}
|
||||
@@ -66,6 +66,8 @@ import {initActionsPermissionsForm} from './features/common-actions-permissions.
|
||||
import {initRefIssueContextPopup} from './features/ref-issue.ts';
|
||||
import {initGlobalShortcut} from './modules/shortcut.ts';
|
||||
import {initDevtest} from './modules/devtest.ts';
|
||||
import {initGpgSignup} from './features/user-auth-gpg-signup.js';
|
||||
import {initGpgSignin} from './features/user-auth-gpg-signin.js';
|
||||
|
||||
const initStartTime = performance.now();
|
||||
const initPerformanceTracer = callInitFunctions([
|
||||
@@ -84,6 +86,8 @@ const initPerformanceTracer = callInitFunctions([
|
||||
initGlobalDeleteButton,
|
||||
initGlobalInput,
|
||||
initGlobalShortcut,
|
||||
initGpgSignup,
|
||||
initGpgSignin,
|
||||
|
||||
initCommonOrganization,
|
||||
initCommonIssueListQuickGoto,
|
||||
|
||||
Reference in New Issue
Block a user