feat: add Kerberos/SPNEGO authentication (#5225)

This commit is contained in:
Yang Luo
2026-03-07 09:46:45 +08:00
parent fa93d4eb8b
commit 394b3e1372
19 changed files with 380 additions and 11 deletions

View File

@@ -118,6 +118,7 @@ p, *, *, GET, /api/run-casbin-command, *, *
p, *, *, POST, /api/refresh-engines, *, *
p, *, *, GET, /api/get-invitation-info, *, *
p, *, *, GET, /api/faceid-signin-begin, *, *
p, *, *, GET, /api/kerberos-login, *, *
`
sa := stringadapter.NewAdapter(ruleText)

105
controllers/kerberos.go Normal file
View File

@@ -0,0 +1,105 @@
// Copyright 2026 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package controllers
import (
"fmt"
"strings"
"github.com/casdoor/casdoor/form"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
// KerberosLogin
// @Title KerberosLogin
// @Tag Login API
// @Description Kerberos/SPNEGO login via Integrated Windows Authentication
// @Param application query string true "application name"
// @Success 200 {object} controllers.Response The Response object
// @router /kerberos-login [get]
func (c *ApiController) KerberosLogin() {
applicationName := c.Ctx.Input.Query("application")
if applicationName == "" {
c.ResponseError(c.T("general:Missing parameter") + ": application")
return
}
application, err := object.GetApplication(fmt.Sprintf("admin/%s", applicationName))
if err != nil {
c.ResponseError(err.Error())
return
}
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), applicationName))
return
}
organization, err := object.GetOrganization(util.GetId("admin", application.Organization))
if err != nil {
c.ResponseError(err.Error())
return
}
if organization == nil {
c.ResponseError(fmt.Sprintf("The organization: %s does not exist", application.Organization))
return
}
if organization.KerberosRealm == "" || organization.KerberosKeytab == "" {
c.ResponseError("Kerberos is not configured for this organization")
return
}
authHeader := c.Ctx.Input.Header("Authorization")
if authHeader == "" || !strings.HasPrefix(authHeader, "Negotiate ") {
c.Ctx.Output.Header("WWW-Authenticate", "Negotiate")
c.Ctx.Output.SetStatus(401)
c.Ctx.Output.Body([]byte("Kerberos authentication required"))
return
}
spnegoToken := strings.TrimPrefix(authHeader, "Negotiate ")
kerberosUsername, err := object.ValidateKerberosToken(organization, spnegoToken)
if err != nil {
c.Ctx.Output.Header("WWW-Authenticate", "Negotiate")
c.ResponseError(fmt.Sprintf("Kerberos authentication failed: %s", err.Error()))
return
}
user, err := object.GetUserByKerberosName(organization.Name, kerberosUsername)
if err != nil {
c.ResponseError(err.Error())
return
}
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), kerberosUsername))
return
}
application.OrganizationObj = organization
authForm := &form.AuthForm{
Type: "code",
Application: applicationName,
Organization: organization.Name,
}
resp := c.HandleLoggedIn(application, user, authForm)
if resp != nil {
c.Data["json"] = resp
c.ServeJSON()
}
}

7
go.mod
View File

@@ -48,6 +48,7 @@ require (
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/google/uuid v1.6.0
github.com/hsluoyz/modsecurity-go v0.0.7
github.com/jcmturner/gokrb5/v8 v8.4.4
github.com/json-iterator/go v1.1.12
github.com/lestrrat-go/jwx v1.2.29
github.com/lib/pq v1.10.9
@@ -184,8 +185,14 @@ require (
github.com/gorilla/websocket v1.5.3 // indirect
github.com/gregdel/pushover v1.3.1 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
github.com/jcmturner/gofork v1.7.6 // indirect
github.com/jcmturner/goidentity/v6 v6.0.1 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect

13
go.sum
View File

@@ -1264,7 +1264,9 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1 h1:LqbZZ9sNMWVjeXS4NN5oVvhMjDyLhmA1LG86oSo+IqY=
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
@@ -1296,6 +1298,8 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
@@ -1318,11 +1322,19 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jcchavezs/mergefs v0.1.0 h1:7oteO7Ocl/fnfFMkoVLJxTveCjrsd//UB0j89xmnpec=
github.com/jcchavezs/mergefs v0.1.0/go.mod h1:eRLTrsA+vFwQZ48hj8p8gki/5v9C2bFtHH5Mnn4bcGk=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=
github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jinzhu/configor v1.2.1 h1:OKk9dsR8i6HPOCZR8BcMtcEImAFjIhbJFZNyn5GCZko=
github.com/jinzhu/configor v1.2.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc=
@@ -1876,6 +1888,7 @@ golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=

109
object/kerberos.go Normal file
View File

@@ -0,0 +1,109 @@
// Copyright 2026 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import (
"encoding/base64"
"fmt"
"strings"
"github.com/jcmturner/gokrb5/v8/credentials"
"github.com/jcmturner/gokrb5/v8/gssapi"
"github.com/jcmturner/gokrb5/v8/keytab"
"github.com/jcmturner/gokrb5/v8/service"
"github.com/jcmturner/gokrb5/v8/spnego"
)
// ctxCredentials is the SPNEGO context key holding the Kerberos credentials.
// This must match the value used internally by gokrb5's spnego package.
// If the gokrb5 library changes this internal constant in a future version,
// this value will need to be updated accordingly.
const ctxCredentials = "github.com/jcmturner/gokrb5/v8/ctxCredentials"
// ValidateKerberosToken validates a base64-encoded SPNEGO token from the
// Authorization header and returns the authenticated Kerberos username.
func ValidateKerberosToken(organization *Organization, spnegoTokenBase64 string) (string, error) {
if organization.KerberosRealm == "" || organization.KerberosKdcHost == "" || organization.KerberosKeytab == "" {
return "", fmt.Errorf("kerberos configuration is incomplete for organization: %s", organization.Name)
}
keytabData, err := base64.StdEncoding.DecodeString(organization.KerberosKeytab)
if err != nil {
return "", fmt.Errorf("failed to decode keytab: %w", err)
}
kt := keytab.New()
err = kt.Unmarshal(keytabData)
if err != nil {
return "", fmt.Errorf("failed to parse keytab: %w", err)
}
servicePrincipal := organization.KerberosServiceName
if servicePrincipal == "" {
servicePrincipal = "HTTP"
}
spnegoSvc := spnego.SPNEGOService(kt, service.KeytabPrincipal(servicePrincipal))
tokenBytes, err := base64.StdEncoding.DecodeString(spnegoTokenBase64)
if err != nil {
return "", fmt.Errorf("failed to decode SPNEGO token: %w", err)
}
var st spnego.SPNEGOToken
err = st.Unmarshal(tokenBytes)
if err != nil {
return "", fmt.Errorf("failed to unmarshal SPNEGO token: %w", err)
}
authed, ctx, status := spnegoSvc.AcceptSecContext(&st)
if status.Code != gssapi.StatusComplete && status.Code != gssapi.StatusContinueNeeded {
return "", fmt.Errorf("SPNEGO validation error: %s", status.Message)
}
if status.Code == gssapi.StatusContinueNeeded {
return "", fmt.Errorf("SPNEGO negotiation requires continuation, which is not supported")
}
if !authed {
return "", fmt.Errorf("SPNEGO token validation failed")
}
creds, ok := ctx.Value(ctxCredentials).(*credentials.Credentials)
if !ok || creds == nil {
return "", fmt.Errorf("no credentials found in SPNEGO context")
}
username := creds.UserName()
if username == "" {
return "", fmt.Errorf("no username found in Kerberos ticket")
}
return username, nil
}
// GetUserByKerberosName looks up a Casdoor user by their Kerberos principal name.
// It strips the realm part (e.g., "user@REALM.COM" -> "user") and searches by username.
func GetUserByKerberosName(organizationName string, kerberosUsername string) (*User, error) {
username := kerberosUsername
if idx := strings.Index(username, "@"); idx >= 0 {
username = username[:idx]
}
user, err := GetUserByFields(organizationName, username)
if err != nil {
return nil, err
}
return user, nil
}

View File

@@ -96,6 +96,11 @@ type Organization struct {
LdapAttributes []string `xorm:"mediumtext" json:"ldapAttributes"`
KerberosRealm string `xorm:"varchar(200)" json:"kerberosRealm"`
KerberosKdcHost string `xorm:"varchar(200)" json:"kerberosKdcHost"`
KerberosKeytab string `xorm:"mediumtext" json:"kerberosKeytab"`
KerberosServiceName string `xorm:"varchar(100)" json:"kerberosServiceName"`
OrgBalance float64 `json:"orgBalance"`
UserBalance float64 `json:"userBalance"`
BalanceCredit float64 `json:"balanceCredit"`

View File

@@ -64,6 +64,7 @@ func InitAPI() {
web.Router("/api/get-captcha-status", &controllers.ApiController{}, "GET:GetCaptchaStatus")
web.Router("/api/callback", &controllers.ApiController{}, "POST:Callback")
web.Router("/api/device-auth", &controllers.ApiController{}, "POST:DeviceAuth")
web.Router("/api/kerberos-login", &controllers.ApiController{}, "GET:KerberosLogin")
web.Router("/api/get-organizations", &controllers.ApiController{}, "GET:GetOrganizations")
web.Router("/api/get-organization", &controllers.ApiController{}, "GET:GetOrganization")

View File

@@ -799,6 +799,46 @@ class OrganizationEditPage extends React.Component {
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("organization:Kerberos realm"), i18next.t("organization:Kerberos realm - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.organization.kerberosRealm} onChange={e => {
this.updateOrganizationField("kerberosRealm", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("organization:Kerberos KDC host"), i18next.t("organization:Kerberos KDC host - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.organization.kerberosKdcHost} onChange={e => {
this.updateOrganizationField("kerberosKdcHost", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("organization:Kerberos keytab"), i18next.t("organization:Kerberos keytab - Tooltip"))} :
</Col>
<Col span={22} >
<Input.TextArea rows={4} value={this.state.organization.kerberosKeytab} onChange={e => {
this.updateOrganizationField("kerberosKeytab", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("organization:Kerberos service name"), i18next.t("organization:Kerberos service name - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.organization.kerberosServiceName} placeholder="HTTP" onChange={e => {
this.updateOrganizationField("kerberosServiceName", e.target.value);
}} />
</Col>
</Row>
</Card>
);
}

View File

@@ -570,7 +570,7 @@
"Physical": "Physisch",
"Show all": "Alle anzeigen",
"Virtual": "Virtuell",
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -\u003e [Groups] page": "Sie müssen zuerst alle Untergruppen löschen. Sie können die Untergruppen im linken Gruppenbaum unter [Organisationen] -\u003e [Gruppen] anzeigen."
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page": "Sie müssen zuerst alle Untergruppen löschen. Sie können die Untergruppen im linken Gruppenbaum unter [Organisationen] -> [Gruppen] anzeigen."
},
"home": {
"New users past 30 days": "Neue Benutzer der letzten 30 Tage",
@@ -770,6 +770,14 @@
"Init score - Tooltip": "Anfangspunkte, die Benutzern bei der Registrierung vergeben werden",
"Is profile public": "Ist das Profil öffentlich?",
"Is profile public - Tooltip": "Nach der Schließung können nur globale Administratoren oder Benutzer in der gleichen Organisation auf die Profilseite des Benutzers zugreifen",
"Kerberos KDC host": "Kerberos KDC host",
"Kerberos KDC host - Tooltip": "The hostname of the Kerberos Key Distribution Center (e.g., kdc.example.com)",
"Kerberos keytab": "Kerberos keytab",
"Kerberos keytab - Tooltip": "The base64-encoded keytab file for the service principal",
"Kerberos realm": "Kerberos realm",
"Kerberos realm - Tooltip": "The Kerberos realm for IWA/SPNEGO authentication (e.g., EXAMPLE.COM)",
"Kerberos service name": "Kerberos service name",
"Kerberos service name - Tooltip": "The Kerberos service principal name (defaults to HTTP)",
"Modify rule": "Regel ändern",
"New Organization": "Neue Organisation",
"Optional": "Optional",

View File

@@ -570,7 +570,7 @@
"Physical": "Physical",
"Show all": "Show all",
"Virtual": "Virtual",
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -\u003e [Groups] page": "You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -\u003e [Groups] page"
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page": "You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page"
},
"home": {
"New users past 30 days": "New users past 30 days",
@@ -770,6 +770,14 @@
"Init score - Tooltip": "Initial score points awarded to users upon registration",
"Is profile public": "Is profile public",
"Is profile public - Tooltip": "After being closed, only global administrators or users in the same organization can access the user's profile page",
"Kerberos KDC host": "Kerberos KDC host",
"Kerberos KDC host - Tooltip": "The hostname of the Kerberos Key Distribution Center (e.g., kdc.example.com)",
"Kerberos keytab": "Kerberos keytab",
"Kerberos keytab - Tooltip": "The base64-encoded keytab file for the service principal",
"Kerberos realm": "Kerberos realm",
"Kerberos realm - Tooltip": "The Kerberos realm for IWA/SPNEGO authentication (e.g., EXAMPLE.COM)",
"Kerberos service name": "Kerberos service name",
"Kerberos service name - Tooltip": "The Kerberos service principal name (defaults to HTTP)",
"Modify rule": "Modify rule",
"New Organization": "New Organization",
"Optional": "Optional",

View File

@@ -570,7 +570,7 @@
"Physical": "Físico",
"Show all": "Mostrar todos",
"Virtual": "Virtual",
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -\u003e [Groups] page": "Necesitas eliminar todos los subgrupos primero. Puedes ver los subgrupos en el árbol de grupos a la izquierda en la página [Organizaciones] -\u003e [Grupos]"
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page": "Necesitas eliminar todos los subgrupos primero. Puedes ver los subgrupos en el árbol de grupos a la izquierda en la página [Organizaciones] -> [Grupos]"
},
"home": {
"New users past 30 days": "Nuevos usuarios en los últimos 30 días",
@@ -770,6 +770,14 @@
"Init score - Tooltip": "Puntos de puntuación inicial otorgados a los usuarios al registrarse",
"Is profile public": "Es el perfil público",
"Is profile public - Tooltip": "Después de estar cerrado, solo los administradores globales o usuarios de la misma organización pueden acceder a la página de perfil del usuario",
"Kerberos KDC host": "Kerberos KDC host",
"Kerberos KDC host - Tooltip": "The hostname of the Kerberos Key Distribution Center (e.g., kdc.example.com)",
"Kerberos keytab": "Kerberos keytab",
"Kerberos keytab - Tooltip": "The base64-encoded keytab file for the service principal",
"Kerberos realm": "Kerberos realm",
"Kerberos realm - Tooltip": "The Kerberos realm for IWA/SPNEGO authentication (e.g., EXAMPLE.COM)",
"Kerberos service name": "Kerberos service name",
"Kerberos service name - Tooltip": "The Kerberos service principal name (defaults to HTTP)",
"Modify rule": "Modificar regla",
"New Organization": "Nueva organización",
"Optional": "Opcional",

View File

@@ -570,7 +570,7 @@
"Physical": "Physique",
"Show all": "Afficher tout",
"Virtual": "Virtuel",
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -\u003e [Groups] page": "Vous devez d'abord supprimer tous les sous-groupes. Vous pouvez voir les sous-groupes dans l'arborescence des groupes à gauche de la page [Organisations] -\u003e [Groupes]"
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page": "Vous devez d'abord supprimer tous les sous-groupes. Vous pouvez voir les sous-groupes dans l'arborescence des groupes à gauche de la page [Organisations] -> [Groupes]"
},
"home": {
"New users past 30 days": "Nouveaux utilisateurs ces 30 derniers jours",
@@ -770,6 +770,14 @@
"Init score - Tooltip": "Score initial attribué au compte lors de leur inscription",
"Is profile public": "Est-ce que le profil est public ?",
"Is profile public - Tooltip": "Après sa fermeture, seuls les administrateurs et administratrices globales ou les comptes de la même organisation peuvent accéder à la page de profil de l'utilisateur",
"Kerberos KDC host": "Kerberos KDC host",
"Kerberos KDC host - Tooltip": "The hostname of the Kerberos Key Distribution Center (e.g., kdc.example.com)",
"Kerberos keytab": "Kerberos keytab",
"Kerberos keytab - Tooltip": "The base64-encoded keytab file for the service principal",
"Kerberos realm": "Kerberos realm",
"Kerberos realm - Tooltip": "The Kerberos realm for IWA/SPNEGO authentication (e.g., EXAMPLE.COM)",
"Kerberos service name": "Kerberos service name",
"Kerberos service name - Tooltip": "The Kerberos service principal name (defaults to HTTP)",
"Modify rule": "Règle de modification",
"New Organization": "Nouvelle organisation",
"Optional": "Optionnel",

View File

@@ -570,7 +570,7 @@
"Physical": "物理",
"Show all": "すべて表示",
"Virtual": "仮想",
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -\u003e [Groups] page": "最初にすべてのサブグループを削除する必要があります。[組織] -\u003e [グループ]ページの左側のグループツリーでサブグループを確認できます"
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page": "最初にすべてのサブグループを削除する必要があります。[組織] -> [グループ]ページの左側のグループツリーでサブグループを確認できます"
},
"home": {
"New users past 30 days": "過去30日間の新規ユーザー",
@@ -770,6 +770,14 @@
"Init score - Tooltip": "登録時にユーザーに与えられる初期スコアポイント",
"Is profile public": "プロフィールは公開されていますか?",
"Is profile public - Tooltip": "閉鎖された後、グローバル管理者または同じ組織のユーザーだけがユーザーのプロファイルページにアクセスできます",
"Kerberos KDC host": "Kerberos KDC host",
"Kerberos KDC host - Tooltip": "The hostname of the Kerberos Key Distribution Center (e.g., kdc.example.com)",
"Kerberos keytab": "Kerberos keytab",
"Kerberos keytab - Tooltip": "The base64-encoded keytab file for the service principal",
"Kerberos realm": "Kerberos realm",
"Kerberos realm - Tooltip": "The Kerberos realm for IWA/SPNEGO authentication (e.g., EXAMPLE.COM)",
"Kerberos service name": "Kerberos service name",
"Kerberos service name - Tooltip": "The Kerberos service principal name (defaults to HTTP)",
"Modify rule": "ルールを変更する",
"New Organization": "新しい組織",
"Optional": "オプション",

View File

@@ -570,7 +570,7 @@
"Physical": "Fizyczna",
"Show all": "Pokaż wszystko",
"Virtual": "Wirtualna",
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -\u003e [Groups] page": "Musisz najpierw usunąć wszystkie podgrupy. Możesz przeglądać podgrupy w lewym drzewie grup na stronie [Organizacje] -\u003e [Grupy]"
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page": "Musisz najpierw usunąć wszystkie podgrupy. Możesz przeglądać podgrupy w lewym drzewie grup na stronie [Organizacje] -> [Grupy]"
},
"home": {
"New users past 30 days": "Nowi użytkownicy w ciągu ostatnich 30 dni",
@@ -770,6 +770,14 @@
"Init score - Tooltip": "Początkowe punkty przyznawane użytkownikom po rejestracji",
"Is profile public": "Profil publiczny",
"Is profile public - Tooltip": "Po wyłączeniu tylko administratorzy globalni lub użytkownicy z tej samej organizacji mogą uzyskać dostęp do strony profilu użytkownika",
"Kerberos KDC host": "Kerberos KDC host",
"Kerberos KDC host - Tooltip": "The hostname of the Kerberos Key Distribution Center (e.g., kdc.example.com)",
"Kerberos keytab": "Kerberos keytab",
"Kerberos keytab - Tooltip": "The base64-encoded keytab file for the service principal",
"Kerberos realm": "Kerberos realm",
"Kerberos realm - Tooltip": "The Kerberos realm for IWA/SPNEGO authentication (e.g., EXAMPLE.COM)",
"Kerberos service name": "Kerberos service name",
"Kerberos service name - Tooltip": "The Kerberos service principal name (defaults to HTTP)",
"Modify rule": "Zasada modyfikacji",
"New Organization": "Nowa organizacja",
"Optional": "Opcjonalne",

View File

@@ -570,7 +570,7 @@
"Physical": "Físico",
"Show all": "Mostrar todos",
"Virtual": "Virtual",
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -\u003e [Groups] page": "Você precisa excluir todos os subgrupos primeiro. Você pode visualizar os subgrupos na árvore de grupos à esquerda na página [Organizações] -\u003e [Grupos]"
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page": "Você precisa excluir todos os subgrupos primeiro. Você pode visualizar os subgrupos na árvore de grupos à esquerda na página [Organizações] -> [Grupos]"
},
"home": {
"New users past 30 days": "Novos usuários nos últimos 30 dias",
@@ -770,6 +770,14 @@
"Init score - Tooltip": "Pontos de pontuação inicial concedidos aos usuários no momento do registro",
"Is profile public": "Perfil é público",
"Is profile public - Tooltip": "Após ser fechado, apenas administradores globais ou usuários na mesma organização podem acessar a página de perfil do usuário",
"Kerberos KDC host": "Kerberos KDC host",
"Kerberos KDC host - Tooltip": "The hostname of the Kerberos Key Distribution Center (e.g., kdc.example.com)",
"Kerberos keytab": "Kerberos keytab",
"Kerberos keytab - Tooltip": "The base64-encoded keytab file for the service principal",
"Kerberos realm": "Kerberos realm",
"Kerberos realm - Tooltip": "The Kerberos realm for IWA/SPNEGO authentication (e.g., EXAMPLE.COM)",
"Kerberos service name": "Kerberos service name",
"Kerberos service name - Tooltip": "The Kerberos service principal name (defaults to HTTP)",
"Modify rule": "Modificar regra",
"New Organization": "Nova Organização",
"Optional": "Opcional",

View File

@@ -570,7 +570,7 @@
"Physical": "Fiziksel",
"Show all": "Tümünü göster",
"Virtual": "Sanal",
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -\u003e [Groups] page": "Önce tüm alt grupları silmeniz gerekir. Alt grupları [Organizasyonlar] -\u003e [Gruplar] sayfasının sol grup ağacından görüntüleyebilirsiniz."
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page": "Önce tüm alt grupları silmeniz gerekir. Alt grupları [Organizasyonlar] -> [Gruplar] sayfasının sol grup ağacından görüntüleyebilirsiniz."
},
"home": {
"New users past 30 days": "Son 30 gündeki yeni kullanıcılar",
@@ -770,6 +770,14 @@
"Init score - Tooltip": "Kullanıcılara kayıt sırasında verilen başlangıç puanları",
"Is profile public": "Profil genel mi",
"Is profile public - Tooltip": "Kapatıldıktan sonra, yalnızca küresel yöneticiler veya aynı organizasyondaki kullanıcılar kullanıcının profil sayfasına erişebilir",
"Kerberos KDC host": "Kerberos KDC host",
"Kerberos KDC host - Tooltip": "The hostname of the Kerberos Key Distribution Center (e.g., kdc.example.com)",
"Kerberos keytab": "Kerberos keytab",
"Kerberos keytab - Tooltip": "The base64-encoded keytab file for the service principal",
"Kerberos realm": "Kerberos realm",
"Kerberos realm - Tooltip": "The Kerberos realm for IWA/SPNEGO authentication (e.g., EXAMPLE.COM)",
"Kerberos service name": "Kerberos service name",
"Kerberos service name - Tooltip": "The Kerberos service principal name (defaults to HTTP)",
"Modify rule": "Kuralı Değiştir",
"New Organization": "Yeni Organizasyon",
"Optional": "İsteğe bağlı",

View File

@@ -570,7 +570,7 @@
"Physical": "фізичний",
"Show all": "Покажи все",
"Virtual": "Віртуальний",
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -\u003e [Groups] page": "Спочатку потрібно видалити всі підгрупи. Підгрупи можна переглянути у лівому дереві груп на сторінці [Організації] -\u003e [Групи]"
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page": "Спочатку потрібно видалити всі підгрупи. Підгрупи можна переглянути у лівому дереві груп на сторінці [Організації] -> [Групи]"
},
"home": {
"New users past 30 days": "Нові користувачі за останні 30 днів",
@@ -770,6 +770,14 @@
"Init score - Tooltip": "Init score - Tooltip",
"Is profile public": "Is profile public",
"Is profile public - Tooltip": "Is profile public - Tooltip",
"Kerberos KDC host": "Kerberos KDC host",
"Kerberos KDC host - Tooltip": "The hostname of the Kerberos Key Distribution Center (e.g., kdc.example.com)",
"Kerberos keytab": "Kerberos keytab",
"Kerberos keytab - Tooltip": "The base64-encoded keytab file for the service principal",
"Kerberos realm": "Kerberos realm",
"Kerberos realm - Tooltip": "The Kerberos realm for IWA/SPNEGO authentication (e.g., EXAMPLE.COM)",
"Kerberos service name": "Kerberos service name",
"Kerberos service name - Tooltip": "The Kerberos service principal name (defaults to HTTP)",
"Modify rule": "Modify rule",
"New Organization": "New Organization",
"Optional": "Optional",

View File

@@ -570,7 +570,7 @@
"Physical": "Vật lý",
"Show all": "Hiển thị tất cả",
"Virtual": "Ảo",
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -\u003e [Groups] page": "Bạn cần xóa tất cả nhóm con trước. Bạn có thể xem các nhóm con trong cây nhóm bên trái của trang [Tổ chức] -\u003e [Nhóm]"
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page": "Bạn cần xóa tất cả nhóm con trước. Bạn có thể xem các nhóm con trong cây nhóm bên trái của trang [Tổ chức] -> [Nhóm]"
},
"home": {
"New users past 30 days": "Người dùng mới trong 30 ngày qua",
@@ -770,6 +770,14 @@
"Init score - Tooltip": "Điểm số ban đầu được trao cho người dùng khi đăng ký",
"Is profile public": "Hồ sơ có công khai không?",
"Is profile public - Tooltip": "Sau khi đóng lại, chỉ các quản trị viên toàn cầu hoặc người dùng trong cùng tổ chức mới có thể truy cập trang hồ sơ người dùng",
"Kerberos KDC host": "Kerberos KDC host",
"Kerberos KDC host - Tooltip": "The hostname of the Kerberos Key Distribution Center (e.g., kdc.example.com)",
"Kerberos keytab": "Kerberos keytab",
"Kerberos keytab - Tooltip": "The base64-encoded keytab file for the service principal",
"Kerberos realm": "Kerberos realm",
"Kerberos realm - Tooltip": "The Kerberos realm for IWA/SPNEGO authentication (e.g., EXAMPLE.COM)",
"Kerberos service name": "Kerberos service name",
"Kerberos service name - Tooltip": "The Kerberos service principal name (defaults to HTTP)",
"Modify rule": "Sửa đổi quy tắc",
"New Organization": "Tổ chức mới",
"Optional": "Tùy chọn",

View File

@@ -570,7 +570,7 @@
"Physical": "实体组",
"Show all": "显示全部",
"Virtual": "虚拟组",
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -\u003e [Groups] page": "您需要先删除所有子组。您可以在 [组织] -\u003e [群组] 页面左侧的群组树中查看子组"
"You need to delete all subgroups first. You can view the subgroups in the left group tree of the [Organizations] -> [Groups] page": "您需要先删除所有子组。您可以在 [组织] -> [群组] 页面左侧的群组树中查看子组"
},
"home": {
"New users past 30 days": "过去 30 天新增的用户",
@@ -770,6 +770,14 @@
"Init score - Tooltip": "用户注册后所拥有的初始积分",
"Is profile public": "是否公开用户个人页",
"Is profile public - Tooltip": "关闭后只有全局管理员或同组织用户才能访问用户主页",
"Kerberos KDC host": "Kerberos KDC host",
"Kerberos KDC host - Tooltip": "The hostname of the Kerberos Key Distribution Center (e.g., kdc.example.com)",
"Kerberos keytab": "Kerberos keytab",
"Kerberos keytab - Tooltip": "The base64-encoded keytab file for the service principal",
"Kerberos realm": "Kerberos realm",
"Kerberos realm - Tooltip": "The Kerberos realm for IWA/SPNEGO authentication (e.g., EXAMPLE.COM)",
"Kerberos service name": "Kerberos service name",
"Kerberos service name - Tooltip": "The Kerberos service principal name (defaults to HTTP)",
"Modify rule": "修改规则",
"New Organization": "添加组织",
"Optional": "可选",