mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-20 11:43:40 +09:00
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>
This commit is contained in:
co-authored by
Nicolas
joestump
Joe Stump
silverwind
Lunny Xiao
parent
db7dbd5a6b
commit
3bec08f998
@@ -0,0 +1,125 @@
|
||||
<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}}
|
||||
@@ -1 +1 @@
|
||||
<a class="tw-font-semibold"{{if gt .ID 0}} href="{{.HomeLink}}"{{end}}>{{.GetDisplayName}}</a>{{if .IsTypeBot}} <span class="ui basic label tw-p-1 tw-align-baseline">bot</span>{{end}}
|
||||
<a class="tw-font-semibold"{{if gt .ID 0}} href="{{.HomeLink}}"{{end}}>{{.GetDisplayName}}</a>{{template "shared/user/user_type_label" .}}
|
||||
|
||||
@@ -1 +1 @@
|
||||
<a class="text muted" href="{{.HomeLink}}">{{.Name}}{{if .FullName}} ({{.FullName}}){{end}}</a>
|
||||
<a class="text muted" href="{{.HomeLink}}">{{.Name}}{{if .FullName}} ({{.FullName}}){{end}}</a>{{template "shared/user/user_type_label" .}}
|
||||
|
||||
@@ -1 +1 @@
|
||||
<a{{if gt .ID 0}} href="{{.HomeLink}}"{{end}}>{{.GetDisplayName}}</a>
|
||||
<a{{if gt .ID 0}} href="{{.HomeLink}}"{{end}}>{{.GetDisplayName}}</a>{{template "shared/user/user_type_label" .}}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<div class="item flex-relaxed-list">
|
||||
<span class="tw-text-center">
|
||||
{{.ContextUser.Name}}
|
||||
{{.ContextUser.Name}}{{template "shared/user/user_type_label" .ContextUser}}
|
||||
{{if .IsAdmin}}
|
||||
<a href="{{AppSubUrl}}/-/admin/users/{{.ContextUser.ID}}" data-tooltip-content="{{ctx.Locale.Tr "admin.users.details"}}">{{svg "octicon-gear" 18}}</a>
|
||||
{{end}}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{{if .IsTypeBot}} <span class="ui basic label tw-py-0 tw-align-baseline">{{ctx.Locale.Tr "concept_user_bot"}}</span>{{end}}
|
||||
Reference in New Issue
Block a user