Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions monkeyai/admin/src/i18n/locales/ar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,16 @@ export const ar = {
baizhiyunScopes:
"نطاقات التفويض الافتراضية: auth_certification openid phone user email",
issuerUrl: "عنوان URL للمُصدر",
scopes: "نطاقات التفويض (Scope)",
scopesDescription:
"افصل بين نطاقات التفويض بمسافات، مثل profile. لا يلزم إضافة openid. عند ترك الحقل فارغًا، تُستخدم القيم الافتراضية openid profile email.",
userinfoFields: "أسماء الحقول العليا في userinfo",
userinfoDescription:
"يُكتشف عنوان URL لخدمة userinfo تلقائيًا من userinfo_endpoint في مستند اكتشاف OIDC للمُصدر، ولا يلزم إدخاله يدويًا. أدخل أسماء الحقول العليا التي تُرجعها userinfo.",
idField: "اسم حقل معرّف المستخدم",
usernameField: "اسم حقل اسم المستخدم",
avatarField: "اسم حقل الصورة الشخصية",
emailField: "اسم حقل البريد الإلكتروني",
clientId: "معرّف العميل",
clientSecret: "سر العميل",
cancel: "إلغاء",
Expand Down
10 changes: 10 additions & 0 deletions monkeyai/admin/src/i18n/locales/de-DE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,16 @@ export const deDE = {
baizhiyunScopes:
"Standardberechtigungen: auth_certification openid phone user email",
issuerUrl: "Issuer-URL",
scopes: "Berechtigungsbereiche (Scopes)",
scopesDescription:
"Scopes durch Leerzeichen trennen, z. B. profile. openid muss nicht hinzugefügt werden. Bei leerer Eingabe gilt der Standard openid profile email.",
userinfoFields: "Namen der obersten userinfo-Felder",
userinfoDescription:
"Die userinfo-URL wird automatisch über userinfo_endpoint in der OIDC-Discovery des Issuers ermittelt; eine manuelle Eingabe ist nicht nötig. Geben Sie die Namen der obersten Felder der userinfo-Antwort ein.",
idField: "Feldname für Benutzer-ID",
usernameField: "Feldname für Benutzername",
avatarField: "Feldname für Profilbild",
emailField: "Feldname für E-Mail-Adresse",
clientId: "Client-ID",
clientSecret: "Client-Secret",
cancel: "Abbrechen",
Expand Down
10 changes: 10 additions & 0 deletions monkeyai/admin/src/i18n/locales/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1524,6 +1524,16 @@ export const enUS = {
baizhiyunScopes:
"Default scopes: auth_certification openid phone user email.",
issuerUrl: "Issuer URL",
scopes: "Scopes",
scopesDescription:
"Separate scopes with spaces, for example: profile. You do not need to add openid. Leave blank to use the default openid profile email.",
userinfoFields: "Top-level userinfo field names",
userinfoDescription:
"The userinfo URL is discovered from the issuer's OIDC discovery userinfo_endpoint; no URL entry is needed. Enter the top-level field names returned by userinfo.",
idField: "User ID field name",
usernameField: "Username field name",
avatarField: "Avatar field name",
emailField: "Email field name",
clientId: "Client ID",
clientSecret: "Client secret",
cancel: "Cancel",
Expand Down
10 changes: 10 additions & 0 deletions monkeyai/admin/src/i18n/locales/es-419.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,16 @@ export const es419 = {
baizhiyunScopes:
"Permisos predeterminados: auth_certification openid phone user email",
issuerUrl: "URL del emisor",
scopes: "Permisos (scopes)",
scopesDescription:
"Separa los permisos con espacios, por ejemplo: profile. No es necesario agregar openid. Si lo dejas vacío, se usarán los valores predeterminados openid profile email.",
userinfoFields: "Nombres de los campos principales de userinfo",
userinfoDescription:
"La URL de userinfo se obtiene automáticamente de userinfo_endpoint en el documento de descubrimiento OIDC del emisor; no es necesario ingresarla. Indica los nombres de los campos principales que devuelve userinfo.",
idField: "Nombre del campo de ID de usuario",
usernameField: "Nombre del campo de nombre de usuario",
avatarField: "Nombre del campo de foto de perfil",
emailField: "Nombre del campo de correo electrónico",
clientId: "ID de cliente",
clientSecret: "Secreto del cliente",
cancel: "Cancelar",
Expand Down
10 changes: 10 additions & 0 deletions monkeyai/admin/src/i18n/locales/fr-FR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,16 @@ export const frFR = {
baizhiyunScopes:
"Autorisations par défaut : auth_certification openid phone user email",
issuerUrl: "URL de l’émetteur",
scopes: "Portées (scopes)",
scopesDescription:
"Séparez les scopes par des espaces, par exemple : profile. Il n’est pas nécessaire d’ajouter openid. Si le champ est vide, la valeur par défaut est openid profile email.",
userinfoFields: "Noms des champs de premier niveau de userinfo",
userinfoDescription:
"L’URL de userinfo est obtenue automatiquement via userinfo_endpoint dans le document de découverte OIDC de l’émetteur ; aucune saisie d’URL n’est nécessaire. Indiquez les noms des champs de premier niveau renvoyés par userinfo.",
idField: "Nom du champ de l’identifiant utilisateur",
usernameField: "Nom du champ du nom d’utilisateur",
avatarField: "Nom du champ de la photo de profil",
emailField: "Nom du champ de l’adresse e-mail",
clientId: "ID client",
clientSecret: "Secret client",
cancel: "Annuler",
Expand Down
10 changes: 10 additions & 0 deletions monkeyai/admin/src/i18n/locales/ja-JP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,16 @@ export const jaJP = {
baizhiyunScopes:
"デフォルトのスコープ: auth_certification openid phone user email",
issuerUrl: "Issuer URL",
scopes: "スコープ",
scopesDescription:
"スコープは半角スペースで区切って入力してください(例:profile)。openid を追加する必要はありません。空欄の場合はデフォルトの openid profile email が使用されます。",
userinfoFields: "userinfo のトップレベルフィールド名",
userinfoDescription:
"userinfo の URL は発行者の OIDC Discovery にある userinfo_endpoint から自動取得されるため、手入力は不要です。userinfo の応答に含まれるトップレベルのフィールド名を入力してください。",
idField: "ユーザー ID のフィールド名",
usernameField: "ユーザー名のフィールド名",
avatarField: "プロフィール画像のフィールド名",
emailField: "メールアドレスのフィールド名",
clientId: "Client ID",
clientSecret: "Client Secret",
cancel: "キャンセル",
Expand Down
10 changes: 10 additions & 0 deletions monkeyai/admin/src/i18n/locales/ko-KR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,16 @@ export const koKR = {
baizhiyunScopes:
"기본 권한 범위: auth_certification openid phone user email",
issuerUrl: "Issuer URL",
scopes: "권한 범위 (Scope)",
scopesDescription:
"권한 범위를 공백으로 구분해 입력하세요(예: profile). openid를 추가할 필요는 없습니다. 비워 두면 기본값 openid profile email이 사용됩니다.",
userinfoFields: "userinfo 최상위 필드 이름",
userinfoDescription:
"userinfo URL은 발급자의 OIDC Discovery에 있는 userinfo_endpoint에서 자동으로 확인하므로 직접 입력할 필요가 없습니다. userinfo 응답의 최상위 필드 이름을 입력하세요.",
idField: "사용자 ID 필드 이름",
usernameField: "사용자 이름 필드 이름",
avatarField: "프로필 이미지 필드 이름",
emailField: "이메일 필드 이름",
clientId: "Client ID",
clientSecret: "Client Secret",
cancel: "취소",
Expand Down
10 changes: 10 additions & 0 deletions monkeyai/admin/src/i18n/locales/ru-RU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,16 @@ export const ruRU = {
baizhiyunScopes:
"Разрешения по умолчанию: auth_certification openid phone user email",
issuerUrl: "URL издателя",
scopes: "Области доступа (scopes)",
scopesDescription:
"Разделяйте области доступа пробелами, например: profile. Добавлять openid необязательно. Если поле пустое, используются значения по умолчанию openid profile email.",
userinfoFields: "Имена полей верхнего уровня в userinfo",
userinfoDescription:
"URL для userinfo определяется автоматически по userinfo_endpoint в документе OIDC Discovery издателя; вводить его вручную не нужно. Укажите имена полей верхнего уровня в ответе userinfo.",
idField: "Имя поля идентификатора пользователя",
usernameField: "Имя поля имени пользователя",
avatarField: "Имя поля аватара",
emailField: "Имя поля электронной почты",
clientId: "Client ID",
clientSecret: "Client Secret",
cancel: "Отмена",
Expand Down
10 changes: 10 additions & 0 deletions monkeyai/admin/src/i18n/locales/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1440,6 +1440,16 @@ export const zhCN = {
baizhiyunScopes:
"默认授权范围:auth_certification openid phone user email。",
issuerUrl: "Issuer URL",
scopes: "授权范围(Scope)",
scopesDescription:
"多个 Scope 用空格分隔,例如:profile;无需强制添加 openid。留空时使用默认的 openid profile email。",
userinfoFields: "userinfo 顶层字段名",
userinfoDescription:
"userinfo 地址从 Issuer 的 OIDC discovery 中的 userinfo_endpoint 自动获取,无需手填 URL。请填写 userinfo 返回数据中的顶层字段名。",
idField: "用户 ID 字段名",
usernameField: "用户名字段名",
avatarField: "头像字段名",
emailField: "邮箱字段名",
clientId: "Client ID",
clientSecret: "Client Secret",
cancel: "取消",
Expand Down
10 changes: 10 additions & 0 deletions monkeyai/admin/src/i18n/locales/zh-TW.ts
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,16 @@ export const zhTW = {
baizhiyunScopes:
"預設授權範圍: auth_certification openid phone user email",
issuerUrl: "Issuer URL",
scopes: "授權範圍(Scope)",
scopesDescription:
"多個 Scope 以空格分隔,例如:profile;不必強制加入 openid。留空時使用預設的 openid profile email。",
userinfoFields: "userinfo 頂層欄位名稱",
userinfoDescription:
"userinfo 網址會自動從 Issuer 的 OIDC Discovery 中的 userinfo_endpoint 取得,無須手動填寫網址。請填寫 userinfo 回傳資料中的頂層欄位名稱。",
idField: "使用者 ID 欄位名稱",
usernameField: "使用者名稱欄位名稱",
avatarField: "頭像欄位名稱",
emailField: "電子郵件欄位名稱",
clientId: "Client ID",
clientSecret: "Client Secret",
cancel: "取消",
Expand Down
151 changes: 148 additions & 3 deletions monkeyai/admin/src/pages/other-settings-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ const OAUTH_PROVIDERS = [
{ value: "oidc", labelKey: "pages.otherSettings.oauth.providers.oidc" },
] as const

const OIDC_FIELD_DEFAULTS = {
idField: "sub",
usernameField: "name",
avatarField: "picture",
emailField: "email",
} as const
const DEFAULT_OIDC_SCOPES = "openid profile email"

const ENCRYPTION_OPTIONS = [
{
value: "starttls",
Expand Down Expand Up @@ -138,6 +146,10 @@ type OAuthConnection = {
clientId: string
clientSecret: string
issuerUrl?: string
idField?: string
usernameField?: string
avatarField?: string
emailField?: string
scopes?: string[]
enabled: boolean
autoRegistrationEnabled: boolean
Expand All @@ -152,6 +164,10 @@ type AutoRegistrationPendingAction =
| { type: "email"; enabled: boolean }
| { type: "oauth"; enabled: boolean; connection: OAuthConnection }

function readOidcField(value: unknown): string | undefined {
return typeof value === "string" && value.trim() ? value : undefined
}

function readOAuthConnections(
value: Record<string, unknown>
): OAuthConnection[] {
Expand All @@ -169,6 +185,14 @@ function readOAuthConnections(
issuerUrl: connection.issuer_url
? String(connection.issuer_url)
: undefined,
...(connection.provider === "oidc"
? {
idField: readOidcField(connection.id_field),
usernameField: readOidcField(connection.username_field),
avatarField: readOidcField(connection.avatar_field),
emailField: readOidcField(connection.email_field),
}
: {}),
scopes: Array.isArray(connection.scopes)
? connection.scopes.map(String)
: undefined,
Expand Down Expand Up @@ -424,6 +448,22 @@ export function OtherSettingsPage() {
client_id: connection.clientId,
client_secret: connection.clientSecret,
issuer_url: connection.issuerUrl ?? null,
...(connection.provider === "oidc"
? {
...(connection.idField?.trim()
? { id_field: connection.idField }
: {}),
...(connection.usernameField?.trim()
? { username_field: connection.usernameField }
: {}),
...(connection.avatarField?.trim()
? { avatar_field: connection.avatarField }
: {}),
...(connection.emailField?.trim()
? { email_field: connection.emailField }
: {}),
}
: {}),
scopes: connection.scopes,
enabled: connection.enabled,
auto_registration_enabled: connection.autoRegistrationEnabled,
Expand Down Expand Up @@ -486,13 +526,23 @@ export function OtherSettingsPage() {
const clientId = String(formData.get("clientId") ?? "").trim()
const clientSecret = String(formData.get("clientSecret") ?? "").trim()
const issuerUrl = String(formData.get("issuerUrl") ?? "").trim()
const oidcFields =
oauthProvider === "oidc"
? {
idField: String(formData.get("idField") ?? "").trim(),
usernameField: String(formData.get("usernameField") ?? "").trim(),
avatarField: String(formData.get("avatarField") ?? "").trim(),
emailField: String(formData.get("emailField") ?? "").trim(),
}
: undefined

if (
!name ||
!clientId ||
(!clientSecret && !editingOauthConnection) ||
((oauthProvider === "oidc" || oauthProvider === "baizhiyun") &&
!issuerUrl)
!issuerUrl) ||
(oidcFields && Object.values(oidcFields).some((field) => !field))
) {
return
}
Expand All @@ -504,7 +554,14 @@ export function OtherSettingsPage() {
clientId,
clientSecret,
issuerUrl: issuerUrl || undefined,
scopes: editingOauthConnection?.scopes,
...oidcFields,
scopes:
oauthProvider === "oidc"
? String(formData.get("scopes") ?? "")
.trim()
.split(/\s+/)
.filter(Boolean)
: editingOauthConnection?.scopes,
enabled: editingOauthConnection?.enabled ?? true,
autoRegistrationEnabled:
editingOauthConnection?.autoRegistrationEnabled ?? true,
Expand Down Expand Up @@ -1591,7 +1648,7 @@ export function OtherSettingsPage() {
{t("pages.otherSettings.oauth.add")}
</DialogTrigger>
<DialogContent
className="sm:max-w-lg"
className="max-h-[calc(100dvh-2rem)] overflow-y-auto sm:max-w-lg"
closeLabel={t("common.close")}
>
<form
Expand Down Expand Up @@ -1695,6 +1752,94 @@ export function OtherSettingsPage() {
required={!editingOauthConnection}
/>
</Field>
{oauthProvider === "oidc" && (
<Field>
<FieldLabel htmlFor="oauth-scopes">
{t("pages.otherSettings.oauth.scopes")}
</FieldLabel>
<Input
id="oauth-scopes"
name="scopes"
defaultValue={
editingOauthConnection?.provider === "oidc" &&
editingOauthConnection.scopes?.length
? editingOauthConnection.scopes.join(" ")
: DEFAULT_OIDC_SCOPES
}
/>
<FieldDescription>
{t("pages.otherSettings.oauth.scopesDescription")}
</FieldDescription>
</Field>
)}
{oauthProvider === "oidc" && (
<FieldSet>
<FieldLegend>
{t("pages.otherSettings.oauth.userinfoFields")}
</FieldLegend>
<FieldDescription>
{t("pages.otherSettings.oauth.userinfoDescription")}
</FieldDescription>
<FieldGroup className="gap-4">
<Field>
<FieldLabel htmlFor="oauth-id-field">
{t("pages.otherSettings.oauth.idField")}
</FieldLabel>
<Input
id="oauth-id-field"
name="idField"
defaultValue={
editingOauthConnection?.idField ??
OIDC_FIELD_DEFAULTS.idField
}
required
/>
</Field>
<Field>
<FieldLabel htmlFor="oauth-username-field">
{t("pages.otherSettings.oauth.usernameField")}
</FieldLabel>
<Input
id="oauth-username-field"
name="usernameField"
defaultValue={
editingOauthConnection?.usernameField ??
OIDC_FIELD_DEFAULTS.usernameField
}
required
/>
</Field>
<Field>
<FieldLabel htmlFor="oauth-avatar-field">
{t("pages.otherSettings.oauth.avatarField")}
</FieldLabel>
<Input
id="oauth-avatar-field"
name="avatarField"
defaultValue={
editingOauthConnection?.avatarField ??
OIDC_FIELD_DEFAULTS.avatarField
}
required
/>
</Field>
<Field>
<FieldLabel htmlFor="oauth-email-field">
{t("pages.otherSettings.oauth.emailField")}
</FieldLabel>
<Input
id="oauth-email-field"
name="emailField"
defaultValue={
editingOauthConnection?.emailField ??
OIDC_FIELD_DEFAULTS.emailField
}
required
/>
</Field>
</FieldGroup>
</FieldSet>
)}
{oauthProvider === "baizhiyun" && (
<FieldDescription>
{t("pages.otherSettings.oauth.baizhiyunScopes")}
Expand Down
Loading
Loading