case 4688 - agent User flag and agent tokens on the user form

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-01 19:19:12 -07:00
parent 3cde5bb54a
commit f7a661a712

View File

@@ -102,6 +102,102 @@
@change="fieldValueChanged('allowLogin')"
></v-checkbox>
</v-col>
<!-- case 4688: AI agent: no login page sign-in, connects to /mcp with an agent token -->
<v-col cols="12" sm="6" lg="4" xl="3">
<v-checkbox
ref="agent"
v-model="obj.agent"
dense
:readonly="formState.readOnly"
:disabled="obj.id === 1"
:label="$sock.t('UserAgent')"
:hint="$sock.t('UserAgentHint')"
persistent-hint
data-cy="agent"
:error-messages="form().serverErrors(this, 'agent')"
@change="fieldValueChanged('agent')"
></v-checkbox>
</v-col>
<v-col v-if="obj.agent && obj.id != 0" cols="12">
<v-card outlined class="pa-3" data-cy="agentTokens">
<div class="text-subtitle-1">
{{ $sock.t("AgentTokens") }}
</div>
<v-alert
v-if="newAgentToken"
type="warning"
dense
class="mt-2"
>
{{ $sock.t("AgentTokenShowOnce") }}
<div
class="text-body-2 mt-2"
style="word-break: break-all"
>
<code>{{ newAgentToken }}</code>
</div>
</v-alert>
<v-simple-table v-if="agentTokens.length > 0" dense>
<tbody>
<tr v-for="t in agentTokens" :key="t.id">
<td>{{ t.name }}</td>
<td class="text-caption">
{{ $sock.t("AgentTokenExpires") }}:
{{
t.expires
? tokenDate(t.expires)
: $sock.t("AgentTokenNever")
}}
<br />
{{ $sock.t("AgentTokenLastUsed") }}:
{{ tokenDate(t.lastUsed) }}
</td>
<td>
<span v-if="t.revoked" class="text-caption">
{{ $sock.t("AgentTokenRevoked") }}
{{ tokenDate(t.revoked) }}
</span>
<v-btn
v-else-if="rights.change"
small
@click="revokeAgentToken(t)"
>{{ $sock.t("AgentTokenRevoke") }}</v-btn
>
</td>
</tr>
</tbody>
</v-simple-table>
<div v-else class="text-caption my-2">
{{ $sock.t("AgentTokenNone") }}
</div>
<v-row v-if="rights.change" dense align="center">
<v-col cols="12" sm="6">
<v-text-field
v-model="newAgentTokenName"
dense
:label="$sock.t('AgentTokenName')"
></v-text-field>
</v-col>
<v-col cols="6" sm="3">
<v-text-field
v-model.number="newAgentTokenDays"
dense
type="number"
min="0"
:label="$sock.t('AgentTokenExpiresDays')"
></v-text-field>
</v-col>
<v-col cols="6" sm="3">
<v-btn
small
color="primary"
@click="createAgentToken()"
>{{ $sock.t("AgentTokenCreate") }}</v-btn
>
</v-col>
</v-row>
</v-card>
</v-col>
<v-col v-if="obj.allowLogin" cols="12" sm="6" lg="4" xl="3">
<v-text-field
id="username"
@@ -446,6 +542,7 @@ export default {
concurrency: 0,
active: true,
allowLogin: true,
agent: false,
name: null,
roles: null,
userType: 2,
@@ -460,6 +557,10 @@ export default {
lastLogin: null
},
agentTokens: [],
newAgentToken: null,
newAgentTokenName: null,
newAgentTokenDays: 90,
optionsObj: {
id: 0,
concurrency: 0,
@@ -641,6 +742,7 @@ export default {
vm.formState.loading = false;
} else {
vm.obj = res.data;
await vm.getAgentTokens();
generateMenu(vm);
window.$gz.form.setFormState({
vm: vm,
@@ -848,6 +950,66 @@ export default {
window.$gz.errorHandler.handleFormError(error, vm);
}
},
//case 4688: agent tokens
tokenDate(d) {
if (!d) {
return "-";
}
return window.$gz.locale.utcDateToShortDateAndTimeLocalized(
d,
window.$gz.locale.getResolvedTimeZoneName(),
window.$gz.locale.getResolvedLanguage(),
window.$gz.locale.getHour12()
);
},
async getAgentTokens() {
const vm = this;
vm.newAgentToken = null;
if (!vm.obj.agent || !vm.obj.id) {
vm.agentTokens = [];
return;
}
const res = await window.$gz.api.get(`agent-token/${vm.obj.id}`);
if (!res.error) {
vm.agentTokens = res.data;
}
},
async createAgentToken() {
const vm = this;
window.$gz.form.deleteAllErrorBoxErrors(vm);
const days =
vm.newAgentTokenDays === "" || vm.newAgentTokenDays == null
? null
: vm.newAgentTokenDays;
const res = await window.$gz.api.post("agent-token", {
userId: vm.obj.id,
name: vm.newAgentTokenName,
expiresDays: days
});
if (res.error) {
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
return;
}
await vm.getAgentTokens();
vm.newAgentToken = res.data.token;
vm.newAgentTokenName = null;
},
async revokeAgentToken(t) {
const vm = this;
if (
(await window.$gz.dialog.confirmGeneric("AgentTokenRevoke")) != true
) {
return;
}
const res = await window.$gz.api.remove(`agent-token/${t.id}`);
if (res.error) {
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
} else {
await vm.getAgentTokens();
}
},
async disableTfa() {
const vm = this;
@@ -1101,6 +1263,19 @@ async function initForm(vm) {
async function fetchTranslatedText() {
await window.$gz.translation.cacheTranslations([
"PasskeyRemoveAll",
"UserAgent",
"UserAgentHint",
"AgentTokens",
"AgentTokenCreate",
"AgentTokenName",
"AgentTokenExpiresDays",
"AgentTokenShowOnce",
"AgentTokenExpires",
"AgentTokenNever",
"AgentTokenLastUsed",
"AgentTokenRevoke",
"AgentTokenRevoked",
"AgentTokenNone",
"Vendor",
"User",
"UserSettings",