Files
gitea/templates/shared/user/access_tokens.tmpl
T
3bec08f998 feat: manage bot accounts from the admin UI, API and CLI (#38966)
Adds first-class bot accounts (`UserTypeBot`): local, password-less
users for automation that authenticate only with access tokens.

1. Admin UI: create bots, filter users by type, manage a bot's access
tokens, convert between user and bot
2. API: `POST /admin/users/{username}/convert-type`, and user objects
gain a GitHub-compatible `type` (`User`, `Organization`, `Bot`)
3. CLI: `gitea admin user change-type`, `--user-type` accepts `User` or
`Bot` case-insensitively
4. Converting keeps the password, 2FA, OAuth2 grants and access tokens,
and since sign-in rejects bots, converting back restores the account.
Only local, non-admin accounts can be converted, and conversions are
audited
5. Session, reverse proxy, SSPI, external source and password reset
sign-in reject non-individual users, so a bot never gets an interactive
session
6. Bots receive no notifications or emails

Co-authored-by: Nicolas <bircni@icloud.com>
Co-authored-by: joestump <joe@joestump.net>
Co-authored-by: Joe Stump <joe@stu.mp>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-09-18 12:43:36 +00:00

126 lines
5.5 KiB
Handlebars

<div id="access-token-panel">
<h4 class="ui top attached header">
{{ctx.Locale.Tr "settings.manage_access_token"}}
</h4>
<div class="ui attached segment">
{{if .NewTokenValue}}
<div class="ui positive message">
<div class="flex-text-block tw-justify-center">
<span>{{ctx.Locale.Tr "settings.generate_token_success"}}</span>
<code id="new-access-token-value">{{.NewTokenValue}}</code>
<button type="button" class="btn interact-fg tw-px-1" aria-label="{{ctx.Locale.Tr "copy"}}" data-clipboard-target="#new-access-token-value" data-tooltip-content="{{ctx.Locale.Tr "copy"}}">{{svg "octicon-copy" 14}}</button>
</div>
</div>
{{end}}
<div class="flex-divided-list items-with-main">
<div class="item">
{{if .IsBot}}{{ctx.Locale.Tr "admin.users.bot_token_desc"}}{{else}}{{ctx.Locale.Tr "settings.tokens_desc"}}{{end}}
</div>
{{range $token := .Tokens}}
<div class="item">
<div class="item-leading">
<span class="{{if $token.HasRecentActivity}}tw-text-green{{end}}" {{if $token.HasRecentActivity}}data-tooltip-content="{{ctx.Locale.Tr "settings.token_state_desc"}}"{{end}}>
{{svg "fontawesome-send" 32}}
</span>
</div>
<div class="item-main">
<details>
<summary><span class="item-title">{{$token.Name}}</span></summary>
<p class="tw-my-1">
{{ctx.Locale.Tr "settings.repo_and_org_access"}}:
{{if $token.DisplayPublicOnly}}
{{ctx.Locale.Tr "settings.permissions_public_only"}}
{{else}}
{{ctx.Locale.Tr "settings.permissions_access_all"}}
{{end}}
</p>
<p class="tw-my-1">{{ctx.Locale.Tr "settings.permissions_list"}}</p>
<ul class="tw-my-1">
{{range $scope := $token.Scope.StringSlice}}
{{if ne $scope $.ScopePublicOnly}}
<li>{{$scope}}</li>
{{end}}
{{end}}
</ul>
</details>
<div class="item-body">
<i>{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort $token.CreatedUnix)}}{{svg "octicon-info"}} {{if $token.HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} <span {{if $token.HasRecentActivity}}class="tw-text-green"{{end}}>{{DateUtils.AbsoluteShort $token.UpdatedUnix}}</span>{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}}</i>
</div>
</div>
<div class="item-trailing">
{{if not $.IsBot}}
<button type="button" class="ui tiny button link-action" data-modal-confirm="#regenerate-token" data-url="{{$.Link}}/regenerate?id={{$token.ID}}">
{{svg "octicon-sync"}}
{{ctx.Locale.Tr "settings.regenerate_token"}}
</button>
{{end}}
<button type="button" class="ui red tiny button link-action" data-modal-confirm="#delete-token" data-url="{{$.Link}}/delete?id={{$token.ID}}">
{{svg "octicon-trash"}}
{{ctx.Locale.Tr "settings.delete_token"}}
</button>
</div>
</div>
{{end}}
</div>
</div>
<div class="ui bottom attached segment">
<details {{if not .Tokens}}open{{end}}>
<summary><h4 class="ui header tw-inline-block tw-my-2">{{ctx.Locale.Tr "settings.generate_new_token"}}</h4></summary>
<form class="ui form ignore-dirty form-fetch-action" action="{{.Link}}" method="post" data-fetch-sync="$body #access-token-panel">
<div class="field">
<label for="name">{{ctx.Locale.Tr "settings.token_name"}}</label>
<input id="name" name="name" required maxlength="255">
</div>
<div class="field">
<div class="tw-my-2">{{ctx.Locale.Tr "settings.repo_and_org_access"}}</div>
<label class="gt-checkbox">
<input type="radio" name="scope-public-only" value="{{.ScopePublicOnly}}"> {{ctx.Locale.Tr "settings.permissions_public_only"}}
</label>
<label class="gt-checkbox">
<input type="radio" name="scope-public-only" value="" checked> {{ctx.Locale.Tr "settings.permissions_access_all"}}
</label>
</div>
<div>
<div class="tw-my-2">{{ctx.Locale.Tr "settings.access_token_desc" (HTMLFormat `href="%s/api/swagger" target="_blank"` AppSubUrl) (HTMLFormat `href="%s" target="_blank"` "https://docs.gitea.com/development/oauth2-provider#scopes")}}</div>
<table class="ui table unstackable tw-my-2">
{{range $category := .ScopeCategories}}
<tr>
<td>{{$category}}</td>
<td><label class="gt-checkbox"><input type="radio" name="scope-{{$category}}" value="" checked> {{ctx.Locale.Tr "settings.permission_no_access"}}</label></td>
<td><label class="gt-checkbox"><input type="radio" name="scope-{{$category}}" value="read:{{$category}}"> {{ctx.Locale.Tr "settings.permission_read"}}</label></td>
<td><label class="gt-checkbox"><input type="radio" name="scope-{{$category}}" value="write:{{$category}}"> {{ctx.Locale.Tr "settings.permission_write"}}</label></td>
</tr>
{{end}}
</table>
</div>
<button type="submit" class="ui primary button">
{{ctx.Locale.Tr "settings.generate_token"}}
</button>
</form>
</details>
</div>
</div>
<div class="ui small modal" id="delete-token">
<div class="header">
{{svg "octicon-trash"}}
{{ctx.Locale.Tr "settings.access_token_deletion"}}
</div>
<div class="content">
<p>{{ctx.Locale.Tr "settings.access_token_deletion_desc"}}</p>
</div>
{{template "base/modal_actions_confirm"}}
</div>
{{if not .IsBot}}
<div class="ui small modal" id="regenerate-token">
<div class="header">
{{ctx.Locale.Tr "settings.access_token_regeneration"}}
</div>
<div class="content">
<p>{{ctx.Locale.Tr "settings.access_token_regeneration_desc"}}</p>
</div>
{{template "base/modal_actions_confirm"}}
</div>
{{end}}