Compare commits

..

6 Commits

Author SHA1 Message Date
Yaodong Yu
ce722897f1 feat: support prefix path for storage files (#1258) 2022-11-04 21:08:39 +08:00
Mr Forest
a8381e875b feat: change all occurrences when a object name is changed (#1252) 2022-11-02 00:17:38 +08:00
Ke Wang
4c81fd7d16 feat: fix generating wrong x.509 private key file header (#1253)
According to the [official x509 documentation](https://pkg.go.dev/crypto/x509#MarshalPKCS1PrivateKey), the private key generated using `x509.MarshalPKCS1PrivateKey` starts with `-----BEGIN RSA PRIVATE KEY-----` instead of `-----BEGIN PRIVATE KEY-----`. Otherwise, it will not be parsed by most tools (like OpenSSL, [jwt.io](https://jwt.io/), etc.) because it does not conform to the specification.
2022-11-01 22:19:38 +08:00
xAmast
25ee4226d3 feat: clear the session of a signin but non-existent user (#1246) 2022-10-29 20:18:02 +08:00
Bingchang Chen
9d5b019243 fix: nil error if init data is empty (#1247) 2022-10-29 20:04:43 +08:00
Mr Forest
6bb7b545b4 feat: restrict DingTalk user log in who is under the DingTalk Org(which ClientId belong) (#1241)
* feat: fix bug in GetAcceptLanguage()

* feat: add appName when logging in with DingTalk

* fix review problems

* format code

* delete useless printf

* modify display name

Co-authored-by: Gucheng Wang <nomeguy@qq.com>
2022-10-28 22:14:05 +08:00
22 changed files with 596 additions and 48 deletions

View File

@@ -245,8 +245,7 @@ func (c *ApiController) Logout() {
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
application := c.GetSessionApplication()
c.SetSessionUsername("")
c.SetSessionData(nil)
c.ClearUserSession()
if application == nil || application.Name == "app-built-in" || application.HomepageUrl == "" {
c.ResponseOk(user)

View File

@@ -63,8 +63,7 @@ func (c *ApiController) GetSessionUsername() string {
if sessionData != nil &&
sessionData.ExpireTime != 0 &&
sessionData.ExpireTime < time.Now().Unix() {
c.SetSessionUsername("")
c.SetSessionData(nil)
c.ClearUserSession()
return ""
}
@@ -85,13 +84,17 @@ func (c *ApiController) GetSessionApplication() *object.Application {
return application
}
func (c *ApiController) ClearUserSession() {
c.SetSessionUsername("")
c.SetSessionData(nil)
}
func (c *ApiController) GetSessionOidc() (string, string) {
sessionData := c.GetSessionData()
if sessionData != nil &&
sessionData.ExpireTime != 0 &&
sessionData.ExpireTime < time.Now().Unix() {
c.SetSessionUsername("")
c.SetSessionData(nil)
c.ClearUserSession()
return "", ""
}
scopeValue := c.GetSession("scope")

View File

@@ -98,6 +98,7 @@ func (c *ApiController) RequireSignedInUser() (*object.User, bool) {
user := object.GetUser(userId)
if user == nil {
c.ClearUserSession()
c.ResponseError(fmt.Sprintf(c.T("UserErr.DoNotExist"), userId))
return nil, false
}

View File

@@ -15,9 +15,12 @@
package idp
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"strings"
"time"
@@ -167,7 +170,10 @@ func (idp *DingTalkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
Email: dtUserInfo.Email,
AvatarUrl: dtUserInfo.AvatarUrl,
}
isUserInOrg, err := idp.isUserInOrg(userInfo.UnionId)
if !isUserInOrg {
return nil, err
}
return &userInfo, nil
}
@@ -194,3 +200,62 @@ func (idp *DingTalkIdProvider) postWithBody(body interface{}, url string) ([]byt
return data, nil
}
func (idp *DingTalkIdProvider) getInnerAppAccessToken() string {
appKey := idp.Config.ClientID
appSecret := idp.Config.ClientSecret
body := make(map[string]string)
body["appKey"] = appKey
body["appSecret"] = appSecret
bodyData, err := json.Marshal(body)
if err != nil {
log.Println(err.Error())
}
reader := bytes.NewReader(bodyData)
request, err := http.NewRequest("POST", "https://api.dingtalk.com/v1.0/oauth2/accessToken", reader)
request.Header.Set("Content-Type", "application/json;charset=UTF-8")
resp, err := idp.Client.Do(request)
respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err.Error())
}
var data struct {
ExpireIn int `json:"expireIn"`
AccessToken string `json:"accessToken"`
}
err = json.Unmarshal(respBytes, &data)
if err != nil {
log.Println(err.Error())
}
return data.AccessToken
}
func (idp *DingTalkIdProvider) isUserInOrg(unionId string) (bool, error) {
body := make(map[string]string)
body["unionid"] = unionId
bodyData, err := json.Marshal(body)
if err != nil {
log.Println(err.Error())
}
reader := bytes.NewReader(bodyData)
accessToken := idp.getInnerAppAccessToken()
request, _ := http.NewRequest("POST", "https://oapi.dingtalk.com/topapi/user/getbyunionid?access_token="+accessToken, reader)
request.Header.Set("Content-Type", "application/json;charset=UTF-8")
resp, err := idp.Client.Do(request)
respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err.Error())
}
var data struct {
ErrCode int `json:"errcode"`
ErrMessage string `json:"errmsg"`
}
err = json.Unmarshal(respBytes, &data)
if err != nil {
log.Println(err.Error())
}
if data.ErrCode == 60121 {
return false, fmt.Errorf("the user is not found in the organization where clientId and clientSecret belong")
}
return true, nil
}

View File

@@ -270,6 +270,13 @@ func UpdateApplication(id string, application *Application) bool {
application.Name = name
}
if name != application.Name {
err := applicationChangeTrigger(name, application.Name)
if err != nil {
return false
}
}
for _, providerItem := range application.Providers {
providerItem.Provider = nil
}
@@ -400,3 +407,55 @@ func ExtendManagedAccountsWithUser(user *User) *User {
return user
}
func applicationChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
organization := new(Organization)
organization.DefaultApplication = newName
_, err = session.Where("default_application=?", oldName).Update(organization)
if err != nil {
return err
}
user := new(User)
user.SignupApplication = newName
_, err = session.Where("signup_application=?", oldName).Update(user)
if err != nil {
return err
}
resource := new(Resource)
resource.Application = newName
_, err = session.Where("application=?", oldName).Update(resource)
if err != nil {
return err
}
var permissions []*Permission
err = adapter.Engine.Find(&permissions)
if err != nil {
return err
}
for i := 0; i < len(permissions); i++ {
permissionResoureces := permissions[i].Resources
for j := 0; j < len(permissionResoureces); j++ {
if permissionResoureces[j] == oldName {
permissionResoureces[j] = newName
}
}
permissions[i].Resources = permissionResoureces
_, err = session.Where("name=?", permissions[i].Name).Update(permissions[i])
if err != nil {
return err
}
}
return session.Commit()
}

View File

@@ -114,6 +114,12 @@ func UpdateCert(id string, cert *Cert) bool {
return false
}
if name != cert.Name {
err := certChangeTrigger(name, cert.Name)
if err != nil {
return false
}
}
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(cert)
if err != nil {
panic(err)
@@ -161,3 +167,22 @@ func getCertByApplication(application *Application) *Cert {
func GetDefaultCert() *Cert {
return getCert("admin", "cert-built-in")
}
func certChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
application := new(Application)
application.Cert = newName
_, err = session.Where("cert=?", oldName).Update(application)
if err != nil {
return err
}
return session.Commit()
}

View File

@@ -56,12 +56,40 @@ func readInitDataFromFile(filePath string) *InitData {
s := util.ReadStringFromPath(filePath)
data := &InitData{}
data := &InitData{
Organizations: []*Organization{},
Applications: []*Application{},
Users: []*User{},
Certs: []*Cert{},
Providers: []*Provider{},
Ldaps: []*Ldap{},
}
err := util.JsonToStruct(s, data)
if err != nil {
panic(err)
}
// transform nil slice to empty slice
for _, organization := range data.Organizations {
if organization.Tags == nil {
organization.Tags = []string{}
}
}
for _, application := range data.Applications {
if application.Providers == nil {
application.Providers = []*ProviderItem{}
}
if application.SignupItems == nil {
application.SignupItems = []*SignupItem{}
}
if application.GrantTypes == nil {
application.GrantTypes = []string{}
}
if application.RedirectUris == nil {
application.RedirectUris = []string{}
}
}
return data
}

View File

@@ -92,6 +92,12 @@ func UpdateModel(id string, modelObj *Model) bool {
return false
}
if name != modelObj.Name {
err := modelChangeTrigger(name, modelObj.Name)
if err != nil {
return false
}
}
// check model grammar
_, err := model.NewModelFromString(modelObj.ModelText)
if err != nil {
@@ -127,3 +133,22 @@ func DeleteModel(model *Model) bool {
func (model *Model) GetId() string {
return fmt.Sprintf("%s/%s", model.Owner, model.Name)
}
func modelChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
permission := new(Permission)
permission.Model = newName
_, err = session.Where("model=?", oldName).Update(permission)
if err != nil {
return err
}
return session.Commit()
}

View File

@@ -16,6 +16,7 @@ package object
import (
"fmt"
"strings"
"github.com/casdoor/casdoor/cred"
"github.com/casdoor/casdoor/i18n"
@@ -134,15 +135,10 @@ func UpdateOrganization(id string, organization *Organization) bool {
}
if name != organization.Name {
go func() {
application := new(Application)
application.Organization = organization.Name
_, _ = adapter.Engine.Where("organization=?", name).Update(application)
user := new(User)
user.Owner = organization.Name
_, _ = adapter.Engine.Where("owner=?", name).Update(user)
}()
err := organizationChangeTrigger(name, organization.Name)
if err != nil {
return false
}
}
if organization.MasterPassword != "" && organization.MasterPassword != "***" {
@@ -252,3 +248,148 @@ func GetDefaultApplication(id string) (*Application, error) {
return defaultApplication, nil
}
func organizationChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
application := new(Application)
application.Organization = newName
_, err = session.Where("organization=?", oldName).Update(application)
if err != nil {
return err
}
user := new(User)
user.Owner = newName
_, err = session.Where("owner=?", oldName).Update(user)
if err != nil {
return err
}
role := new(Role)
_, err = adapter.Engine.Where("owner=?", oldName).Get(role)
if err != nil {
return err
}
for i, u := range role.Users {
// u = organization/username
split := strings.Split(u, "/")
if split[0] == oldName {
split[0] = newName
role.Users[i] = split[0] + "/" + split[1]
}
}
for i, u := range role.Roles {
// u = organization/username
split := strings.Split(u, "/")
if split[0] == oldName {
split[0] = newName
role.Roles[i] = split[0] + "/" + split[1]
}
}
role.Owner = newName
_, err = session.Where("owner=?", oldName).Update(role)
if err != nil {
return err
}
permission := new(Permission)
_, err = adapter.Engine.Where("owner=?", oldName).Get(permission)
if err != nil {
return err
}
for i, u := range permission.Users {
// u = organization/username
split := strings.Split(u, "/")
if split[0] == oldName {
split[0] = newName
permission.Users[i] = split[0] + "/" + split[1]
}
}
for i, u := range permission.Roles {
// u = organization/username
split := strings.Split(u, "/")
if split[0] == oldName {
split[0] = newName
permission.Roles[i] = split[0] + "/" + split[1]
}
}
permission.Owner = newName
_, err = session.Where("owner=?", oldName).Update(permission)
if err != nil {
return err
}
casbinAdapter := new(CasbinAdapter)
casbinAdapter.Owner = newName
casbinAdapter.Organization = newName
_, err = session.Where("owner=?", oldName).Update(casbinAdapter)
if err != nil {
return err
}
ldap := new(Ldap)
ldap.Owner = newName
_, err = session.Where("owner=?", oldName).Update(ldap)
if err != nil {
return err
}
model := new(Model)
model.Owner = newName
_, err = session.Where("owner=?", oldName).Update(model)
if err != nil {
return err
}
payment := new(Payment)
payment.Organization = newName
_, err = session.Where("organization=?", oldName).Update(payment)
if err != nil {
return err
}
record := new(Record)
record.Owner = newName
record.Organization = newName
_, err = session.Where("organization=?", oldName).Update(record)
if err != nil {
return err
}
resource := new(Resource)
resource.Owner = newName
_, err = session.Where("owner=?", oldName).Update(resource)
if err != nil {
return err
}
syncer := new(Syncer)
syncer.Organization = newName
_, err = session.Where("organization=?", oldName).Update(syncer)
if err != nil {
return err
}
token := new(Token)
token.Organization = newName
_, err = session.Where("organization=?", oldName).Update(token)
if err != nil {
return err
}
webhook := new(Webhook)
webhook.Organization = newName
_, err = session.Where("organization=?", oldName).Update(webhook)
if err != nil {
return err
}
return session.Commit()
}

View File

@@ -60,6 +60,7 @@ type Provider struct {
IntranetEndpoint string `xorm:"varchar(100)" json:"intranetEndpoint"`
Domain string `xorm:"varchar(100)" json:"domain"`
Bucket string `xorm:"varchar(100)" json:"bucket"`
PathPrefix string `xorm:"varchar(100)" json:"pathPrefix"`
Metadata string `xorm:"mediumtext" json:"metadata"`
IdP string `xorm:"mediumtext" json:"idP"`
@@ -175,6 +176,13 @@ func UpdateProvider(id string, provider *Provider) bool {
return false
}
if name != provider.Name {
err := providerChangeTrigger(name, provider.Name)
if err != nil {
return false
}
}
session := adapter.Engine.ID(core.PK{owner, name}).AllCols()
if provider.ClientSecret == "***" {
session = session.Omit("client_secret")
@@ -262,3 +270,41 @@ func GetCaptchaProviderByApplication(applicationId, isCurrentProvider, lang stri
}
return nil, nil
}
func providerChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
var applications []*Application
err = adapter.Engine.Find(&applications)
if err != nil {
return err
}
for i := 0; i < len(applications); i++ {
providers := applications[i].Providers
for j := 0; j < len(providers); j++ {
if providers[j].Name == oldName {
providers[j].Name = newName
}
}
applications[i].Providers = providers
_, err = session.Where("name=?", applications[i].Name).Update(applications[i])
if err != nil {
return err
}
}
resource := new(Resource)
resource.Provider = newName
_, err = session.Where("provider=?", oldName).Update(resource)
if err != nil {
return err
}
return session.Commit()
}

View File

@@ -16,6 +16,7 @@ package object
import (
"fmt"
"strings"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
@@ -94,6 +95,13 @@ func UpdateRole(id string, role *Role) bool {
return false
}
if name != role.Name {
err := roleChangeTrigger(name, role.Name)
if err != nil {
return false
}
}
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(role)
if err != nil {
panic(err)
@@ -133,3 +141,54 @@ func GetRolesByUser(userId string) []*Role {
return roles
}
func roleChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
var roles []*Role
err = adapter.Engine.Find(&roles)
if err != nil {
return err
}
for _, role := range roles {
for j, u := range role.Roles {
split := strings.Split(u, "/")
if split[1] == oldName {
split[1] = newName
role.Roles[j] = split[0] + "/" + split[1]
}
}
_, err = session.Where("name=?", role.Name).Update(role)
if err != nil {
return err
}
}
var permissions []*Permission
err = adapter.Engine.Find(&permissions)
if err != nil {
return err
}
for _, permission := range permissions {
for j, u := range permission.Roles {
// u = organization/username
split := strings.Split(u, "/")
if split[1] == oldName {
split[1] = newName
permission.Roles[j] = split[0] + "/" + split[1]
}
}
_, err = session.Where("name=?", permission.Name).Update(permission)
if err != nil {
return err
}
}
return session.Commit()
}

View File

@@ -55,7 +55,7 @@ func escapePath(path string) string {
}
func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
escapedPath := escapePath(fullFilePath)
escapedPath := util.UrlJoin(provider.PathPrefix, escapePath(fullFilePath))
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
host := ""
@@ -70,7 +70,7 @@ func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
host = util.UrlJoin(provider.Domain, "/files")
}
if provider.Type == "Azure Blob" {
host = fmt.Sprintf("%s/%s", host, provider.Bucket)
host = util.UrlJoin(host, provider.Bucket)
}
fileUrl := util.UrlJoin(host, escapePath(objectKey))

View File

@@ -37,7 +37,7 @@ func generateRsaKeys(bitSize int, expireInYears int, commonName string, organiza
// Encode private key to PKCS#1 ASN.1 PEM.
privateKeyPem := pem.EncodeToMemory(
&pem.Block{
Type: "PRIVATE KEY",
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
},
)

View File

@@ -380,6 +380,13 @@ func UpdateUser(id string, user *User, columns []string, isGlobalAdmin bool) boo
return false
}
if name != user.Name {
err := userChangeTrigger(name, user.Name)
if err != nil {
return false
}
}
if user.Password == "***" {
user.Password = oldUser.Password
}
@@ -416,6 +423,13 @@ func UpdateUserForAllFields(id string, user *User) bool {
return false
}
if name != user.Name {
err := userChangeTrigger(name, user.Name)
if err != nil {
return false
}
}
user.UpdateUserHash()
if user.Avatar != oldUser.Avatar && user.Avatar != "" {
@@ -567,3 +581,62 @@ func ExtendUserWithRolesAndPermissions(user *User) {
user.Roles = GetRolesByUser(user.GetId())
user.Permissions = GetPermissionsByUser(user.GetId())
}
func userChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
var roles []*Role
err = adapter.Engine.Find(&roles)
if err != nil {
return err
}
for _, role := range roles {
for j, u := range role.Users {
// u = organization/username
split := strings.Split(u, "/")
if split[1] == oldName {
split[1] = newName
role.Users[j] = split[0] + "/" + split[1]
}
}
_, err = session.Where("name=?", role.Name).Update(role)
if err != nil {
return err
}
}
var permissions []*Permission
err = adapter.Engine.Find(&permissions)
if err != nil {
return err
}
for _, permission := range permissions {
for j, u := range permission.Users {
// u = organization/username
split := strings.Split(u, "/")
if split[1] == oldName {
split[1] = newName
permission.Users[j] = split[0] + "/" + split[1]
}
}
_, err = session.Where("name=?", permission.Name).Update(permission)
if err != nil {
return err
}
}
resource := new(Resource)
resource.User = newName
_, err = session.Where("user=?", oldName).Update(resource)
if err != nil {
return err
}
return session.Commit()
}

View File

@@ -469,6 +469,16 @@ class ProviderEditPage extends React.Component {
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Path prefix"), i18next.t("provider:The prefix path of the file - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.pathPrefix} onChange={e => {
this.updateProviderField("pathPrefix", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :

View File

@@ -13,6 +13,7 @@
"Sync": "Sync"
},
"application": {
"Always": "Always",
"Auto signin": "Auto signin",
"Auto signin - Tooltip": "Auto signin - Tooltip",
"Background URL": "Background URL",
@@ -43,6 +44,7 @@
"Grant types - Tooltip": "Grant types - Tooltip",
"Left": "Left",
"New Application": "New Application",
"None": "None",
"Password ON": "Passwort AN",
"Password ON - Tooltip": "Whether to allow password login",
"Please select a HTML file": "Bitte wählen Sie eine HTML-Datei",
@@ -53,6 +55,7 @@
"Refresh token expire": "Aktualisierungs-Token läuft ab",
"Refresh token expire - Tooltip": "Aktualisierungs-Token läuft ab - Tooltip",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",
"SAML metadata - Tooltip": "SAML metadata - Tooltip",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
@@ -67,10 +70,7 @@
"Token expire": "Token läuft ab",
"Token expire - Tooltip": "Token läuft ab - Tooltip",
"Token format": "Token-Format",
"Token format - Tooltip": "Token-Format - Tooltip",
"Rule": "Rule",
"None": "None",
"Always": "Always"
"Token format - Tooltip": "Token-Format - Tooltip"
},
"cert": {
"Bit size": "Bitgröße",
@@ -491,6 +491,7 @@
"New Provider": "New Provider",
"Parse": "Parse",
"Parse Metadata successfully": "Metadaten erfolgreich analysieren",
"Path prefix": "Path prefix",
"Port": "Port",
"Port - Tooltip": "Unique string-style identifier",
"Provider URL": "Provider-URL",
@@ -535,6 +536,7 @@
"Test Connection": "Test Smtp Connection",
"Test Email": "Test email config",
"Test Email - Tooltip": "Email Address",
"The prefix path of the file - Tooltip": "The prefix path of the file - Tooltip",
"Token URL": "Token URL",
"Token URL - Tooltip": "Token URL - Tooltip",
"Type": "Typ",

View File

@@ -13,6 +13,7 @@
"Sync": "Sync"
},
"application": {
"Always": "Always",
"Auto signin": "Auto signin",
"Auto signin - Tooltip": "Auto signin - Tooltip",
"Background URL": "Background URL",
@@ -43,6 +44,7 @@
"Grant types - Tooltip": "Grant types - Tooltip",
"Left": "Left",
"New Application": "New Application",
"None": "None",
"Password ON": "Password ON",
"Password ON - Tooltip": "Password ON - Tooltip",
"Please select a HTML file": "Please select a HTML file",
@@ -53,6 +55,7 @@
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expire - Tooltip",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",
"SAML metadata - Tooltip": "SAML metadata - Tooltip",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
@@ -67,10 +70,7 @@
"Token expire": "Token expire",
"Token expire - Tooltip": "Token expire - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "Token format - Tooltip",
"Rule": "Rule",
"None": "None",
"Always": "Always"
"Token format - Tooltip": "Token format - Tooltip"
},
"cert": {
"Bit size": "Bit size",
@@ -491,6 +491,7 @@
"New Provider": "New Provider",
"Parse": "Parse",
"Parse Metadata successfully": "Parse Metadata successfully",
"Path prefix": "Path prefix",
"Port": "Port",
"Port - Tooltip": "Port - Tooltip",
"Provider URL": "Provider URL",
@@ -535,6 +536,7 @@
"Test Connection": "Test Connection",
"Test Email": "Test Email",
"Test Email - Tooltip": "Test Email - Tooltip",
"The prefix path of the file - Tooltip": "The prefix path of the file - Tooltip",
"Token URL": "Token URL",
"Token URL - Tooltip": "Token URL - Tooltip",
"Type": "Type",

View File

@@ -13,6 +13,7 @@
"Sync": "Sync"
},
"application": {
"Always": "Always",
"Auto signin": "Auto signin",
"Auto signin - Tooltip": "Auto signin - Tooltip",
"Background URL": "Background URL",
@@ -43,6 +44,7 @@
"Grant types - Tooltip": "Grant types - Tooltip",
"Left": "Left",
"New Application": "New Application",
"None": "None",
"Password ON": "Mot de passe activé",
"Password ON - Tooltip": "Whether to allow password login",
"Please select a HTML file": "Veuillez sélectionner un fichier HTML",
@@ -53,6 +55,7 @@
"Refresh token expire": "Expiration du jeton d'actualisation",
"Refresh token expire - Tooltip": "Expiration du jeton d'actualisation - infobulle",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",
"SAML metadata - Tooltip": "SAML metadata - Tooltip",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
@@ -67,10 +70,7 @@
"Token expire": "Expiration du jeton",
"Token expire - Tooltip": "Expiration du jeton - Info-bulle",
"Token format": "Format du jeton",
"Token format - Tooltip": "Format du jeton - infobulle",
"Rule": "Rule",
"None": "None",
"Always": "Always"
"Token format - Tooltip": "Format du jeton - infobulle"
},
"cert": {
"Bit size": "Taille du bit",
@@ -491,6 +491,7 @@
"New Provider": "New Provider",
"Parse": "Parse",
"Parse Metadata successfully": "Analyse des métadonnées réussie",
"Path prefix": "Path prefix",
"Port": "Port",
"Port - Tooltip": "Unique string-style identifier",
"Provider URL": "URL du fournisseur",
@@ -535,6 +536,7 @@
"Test Connection": "Test Smtp Connection",
"Test Email": "Test email config",
"Test Email - Tooltip": "Email Address",
"The prefix path of the file - Tooltip": "The prefix path of the file - Tooltip",
"Token URL": "Token URL",
"Token URL - Tooltip": "Token URL - Tooltip",
"Type": "Type de texte",

View File

@@ -13,6 +13,7 @@
"Sync": "Sync"
},
"application": {
"Always": "Always",
"Auto signin": "Auto signin",
"Auto signin - Tooltip": "Auto signin - Tooltip",
"Background URL": "Background URL",
@@ -43,6 +44,7 @@
"Grant types - Tooltip": "Grant types - Tooltip",
"Left": "Left",
"New Application": "New Application",
"None": "None",
"Password ON": "パスワードON",
"Password ON - Tooltip": "Whether to allow password login",
"Please select a HTML file": "HTMLファイルを選択してください",
@@ -53,6 +55,7 @@
"Refresh token expire": "トークンの更新の期限が切れます",
"Refresh token expire - Tooltip": "トークンの有効期限を更新する - ツールチップ",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",
"SAML metadata - Tooltip": "SAML metadata - Tooltip",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
@@ -67,10 +70,7 @@
"Token expire": "トークンの有効期限",
"Token expire - Tooltip": "トークンの有効期限 - ツールチップ",
"Token format": "トークンのフォーマット",
"Token format - Tooltip": "トークンフォーマット - ツールチップ",
"Rule": "Rule",
"None": "None",
"Always": "Always"
"Token format - Tooltip": "トークンフォーマット - ツールチップ"
},
"cert": {
"Bit size": "ビットサイズ",
@@ -491,6 +491,7 @@
"New Provider": "New Provider",
"Parse": "Parse",
"Parse Metadata successfully": "メタデータの解析に成功",
"Path prefix": "Path prefix",
"Port": "ポート",
"Port - Tooltip": "Unique string-style identifier",
"Provider URL": "プロバイダー URL",
@@ -535,6 +536,7 @@
"Test Connection": "Test Smtp Connection",
"Test Email": "Test email config",
"Test Email - Tooltip": "Email Address",
"The prefix path of the file - Tooltip": "The prefix path of the file - Tooltip",
"Token URL": "Token URL",
"Token URL - Tooltip": "Token URL - Tooltip",
"Type": "タイプ",

View File

@@ -13,6 +13,7 @@
"Sync": "Sync"
},
"application": {
"Always": "Always",
"Auto signin": "Auto signin",
"Auto signin - Tooltip": "Auto signin - Tooltip",
"Background URL": "Background URL",
@@ -43,6 +44,7 @@
"Grant types - Tooltip": "Grant types - Tooltip",
"Left": "Left",
"New Application": "New Application",
"None": "None",
"Password ON": "Password ON",
"Password ON - Tooltip": "Whether to allow password login",
"Please select a HTML file": "Please select a HTML file",
@@ -53,6 +55,7 @@
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expire - Tooltip",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",
"SAML metadata - Tooltip": "SAML metadata - Tooltip",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
@@ -67,10 +70,7 @@
"Token expire": "Token expire",
"Token expire - Tooltip": "Token expire - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "Token format - Tooltip",
"Rule": "Rule",
"None": "None",
"Always": "Always"
"Token format - Tooltip": "Token format - Tooltip"
},
"cert": {
"Bit size": "Bit size",
@@ -491,6 +491,7 @@
"New Provider": "New Provider",
"Parse": "Parse",
"Parse Metadata successfully": "Parse Metadata successfully",
"Path prefix": "Path prefix",
"Port": "Port",
"Port - Tooltip": "Unique string-style identifier",
"Provider URL": "Provider URL",
@@ -535,6 +536,7 @@
"Test Connection": "Test Smtp Connection",
"Test Email": "Test email config",
"Test Email - Tooltip": "Email Address",
"The prefix path of the file - Tooltip": "The prefix path of the file - Tooltip",
"Token URL": "Token URL",
"Token URL - Tooltip": "Token URL - Tooltip",
"Type": "Type",

View File

@@ -13,6 +13,7 @@
"Sync": "Sync"
},
"application": {
"Always": "Always",
"Auto signin": "Auto signin",
"Auto signin - Tooltip": "Auto signin - Tooltip",
"Background URL": "Background URL",
@@ -43,6 +44,7 @@
"Grant types - Tooltip": "Виды грантов - Подсказка",
"Left": "Left",
"New Application": "Новое приложение",
"None": "None",
"Password ON": "Пароль ВКЛ",
"Password ON - Tooltip": "Whether to allow password login",
"Please select a HTML file": "Пожалуйста, выберите HTML-файл",
@@ -53,6 +55,7 @@
"Refresh token expire": "Срок действия обновления токена истекает",
"Refresh token expire - Tooltip": "Срок обновления токена истекает - Подсказка",
"Right": "Right",
"Rule": "правило",
"SAML metadata": "Метаданные SAML",
"SAML metadata - Tooltip": "Метаданные SAML - Подсказка",
"SAML metadata URL copied to clipboard successfully": "Адрес метаданных SAML скопирован в буфер обмена",
@@ -67,10 +70,7 @@
"Token expire": "Токен истекает",
"Token expire - Tooltip": "Истек токен - Подсказка",
"Token format": "Формат токена",
"Token format - Tooltip": "Формат токена - Подсказка",
"Rule": "правило",
"None": "None",
"Always": "Always"
"Token format - Tooltip": "Формат токена - Подсказка"
},
"cert": {
"Bit size": "Размер бита",
@@ -491,6 +491,7 @@
"New Provider": "New Provider",
"Parse": "Parse",
"Parse Metadata successfully": "Анализ метаданных успешно завершен",
"Path prefix": "Path prefix",
"Port": "Порт",
"Port - Tooltip": "Unique string-style identifier",
"Provider URL": "URL провайдера",
@@ -535,6 +536,7 @@
"Test Connection": "Test Smtp Connection",
"Test Email": "Test email config",
"Test Email - Tooltip": "Email Address",
"The prefix path of the file - Tooltip": "The prefix path of the file - Tooltip",
"Token URL": "Token URL",
"Token URL - Tooltip": "Token URL - Tooltip",
"Type": "Тип",

View File

@@ -13,6 +13,7 @@
"Sync": "同步"
},
"application": {
"Always": "始终开启",
"Auto signin": "启用自动登录",
"Auto signin - Tooltip": "当Casdoor存在已登录会话时自动采用该会话进行应用端的登录",
"Background URL": "背景图URL",
@@ -43,6 +44,7 @@
"Grant types - Tooltip": "选择允许哪些OAuth协议中的Grant types",
"Left": "居左",
"New Application": "添加应用",
"None": "关闭",
"Password ON": "开启密码",
"Password ON - Tooltip": "是否允许密码登录",
"Please select a HTML file": "请选择一个HTML文件",
@@ -53,6 +55,7 @@
"Refresh token expire": "Refresh Token过期",
"Refresh token expire - Tooltip": "Refresh Token过期时间",
"Right": "居右",
"Rule": "规则",
"SAML metadata": "SAML元数据",
"SAML metadata - Tooltip": "SAML协议的元数据Metadata信息",
"SAML metadata URL copied to clipboard successfully": "SAML元数据URL已成功复制到剪贴板",
@@ -67,10 +70,7 @@
"Token expire": "Access Token过期",
"Token expire - Tooltip": "Access Token过期时间",
"Token format": "Access Token格式",
"Token format - Tooltip": "Access Token格式",
"Rule": "规则",
"None": "关闭",
"Always": "始终开启"
"Token format - Tooltip": "Access Token格式"
},
"cert": {
"Bit size": "位大小",
@@ -491,6 +491,7 @@
"New Provider": "添加提供商",
"Parse": "Parse",
"Parse Metadata successfully": "解析元数据成功",
"Path prefix": "路径前缀",
"Port": "端口",
"Port - Tooltip": "端口号",
"Provider URL": "提供商URL",
@@ -535,6 +536,7 @@
"Test Connection": "测试SMTP连接",
"Test Email": "测试Email配置",
"Test Email - Tooltip": "邮箱地址",
"The prefix path of the file - Tooltip": "文件的路径前缀 - 工具提示",
"Token URL": "Token URL",
"Token URL - Tooltip": "Token URL - 工具提示",
"Type": "类型",