Compare commits

...

13 Commits

Author SHA1 Message Date
b794ef87ee feat: Revert "feat: support reCAPTCHA v3 captcha provider" (#3135)
This reverts commit a0d6f2125e.
2024-08-20 17:56:53 +08:00
a0d6f2125e feat: support reCAPTCHA v3 captcha provider (#3130) 2024-08-20 17:29:37 +08:00
85cbb7d074 feat: add replaceAll polyfill to be compatible with Firefox 68 2024-08-17 18:37:21 +08:00
fdc1be9452 feat: add provider.Bucket to fileUrl response and TrimPrefix "/" before delete GCS object (#3129)
* feat: add provider.Bucket to fileUrl response

* feat: TrimPrefix "/" before Google Cloud Storage delete object
2024-08-17 11:46:58 +08:00
2bd7dabd33 feat: allow custom Domain of Google Cloud Storage Provider (#3128) 2024-08-15 23:28:36 +08:00
9b9a58e7ac feat: update casdoor/oss version to support Google Cloud's Application Default Credentials (#3125) 2024-08-15 13:45:27 +08:00
38e389e8c8 feat: Pagination not updating after last item deletion (#3120) 2024-08-13 16:09:16 +08:00
ab5fcf848e feat: support accessKey and accessSecret login in AutoSigninFilter (#3117) 2024-08-12 12:20:41 +08:00
b4e51b4631 feat: improve error message in GetFailedSigninConfigByUser() 2024-08-10 09:31:46 +08:00
45e25acc80 feat: fix JWT generate issue cause by shared application (#3113)
* fix: fix jwt generate cause by shared application

* fix: fix built-in org will not add -org-
2024-08-09 22:48:44 +08:00
97dcf24a91 feat: improve error message in GetAuthorizationCodeToken() 2024-08-09 21:06:23 +08:00
4c0fff66ff feat: support shared application across organizations (#3108)
* feat: support share application

* revert: revert i18n

* fix: improve code format

* fix: improve code format and move GetSharedOrgFromApp to string.go
2024-08-09 15:43:25 +08:00
e7230700e0 feat: Revert "feat: fix Beego session delete concurrent issue" (#3105)
This reverts commit f21aa9c0d2.
2024-08-07 16:51:54 +08:00
43 changed files with 259 additions and 119 deletions

View File

@ -60,7 +60,6 @@ func (c *ApiController) Unlink() {
c.ResponseError(err.Error())
return
}
if application == nil {
c.ResponseError(c.T("link:You can't unlink yourself, you are not a member of any application"))
return

4
go.mod
View File

@ -6,13 +6,13 @@ require (
github.com/Masterminds/squirrel v1.5.3
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
github.com/aws/aws-sdk-go v1.45.5
github.com/beego/beego v1.12.13
github.com/beego/beego v1.12.12
github.com/beevik/etree v1.1.0
github.com/casbin/casbin/v2 v2.77.2
github.com/casdoor/go-sms-sender v0.24.0
github.com/casdoor/gomail/v2 v2.0.1
github.com/casdoor/notify v0.45.0
github.com/casdoor/oss v1.6.0
github.com/casdoor/oss v1.7.0
github.com/casdoor/xorm-adapter/v3 v3.1.0
github.com/casvisor/casvisor-go-sdk v1.4.0
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f

8
go.sum
View File

@ -1052,8 +1052,8 @@ github.com/baidubce/bce-sdk-go v0.9.156 h1:f++WfptxGmSp5acsjl4kUxHpWDDccoFqkIrQK
github.com/baidubce/bce-sdk-go v0.9.156/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/beego/beego v1.12.13 h1:g39O1LGLTiPejWVqQKK/TFGrroW9BCZQz6/pf4S8IRM=
github.com/beego/beego v1.12.13/go.mod h1:QURFL1HldOcCZAxnc1cZ7wrplsYR5dKPHFjmk6WkLAs=
github.com/beego/beego v1.12.12 h1:ARY1sNVSS23N0mEQIhSqRDTyyDlx95JY0V3GogBbZbQ=
github.com/beego/beego v1.12.12/go.mod h1:QURFL1HldOcCZAxnc1cZ7wrplsYR5dKPHFjmk6WkLAs=
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
@ -1091,8 +1091,8 @@ github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR
github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q=
github.com/casdoor/notify v0.45.0 h1:OlaFvcQFjGOgA4mRx07M8AH1gvb5xNo21mcqrVGlLgk=
github.com/casdoor/notify v0.45.0/go.mod h1:wNHQu0tiDROMBIvz0j3Om3Lhd5yZ+AIfnFb8MYb8OLQ=
github.com/casdoor/oss v1.6.0 h1:IOWrGLJ+VO82qS796eaRnzFPPA1Sn3cotYTi7O/VIlQ=
github.com/casdoor/oss v1.6.0/go.mod h1:rJAWA0hLhtu94t6IRpotLUkXO1NWMASirywQYaGizJE=
github.com/casdoor/oss v1.7.0 h1:VCOuD+CcD0MAA99p6JTyUak14bVR6UsaeyuTaVg0Mrs=
github.com/casdoor/oss v1.7.0/go.mod h1:rJAWA0hLhtu94t6IRpotLUkXO1NWMASirywQYaGizJE=
github.com/casdoor/xorm-adapter/v3 v3.1.0 h1:NodWayRtSLVSeCvL9H3Hc61k0G17KhV9IymTCNfh3kk=
github.com/casdoor/xorm-adapter/v3 v3.1.0/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM=
github.com/casvisor/casvisor-go-sdk v1.4.0 h1:hbZEGGJ1cwdHFAxeXrMoNw6yha6Oyg2F0qQhBNCN/dg=

View File

@ -91,6 +91,7 @@ type Application struct {
CertPublicKey string `xorm:"-" json:"certPublicKey"`
Tags []string `xorm:"mediumtext" json:"tags"`
SamlAttributes []*SamlItem `xorm:"varchar(1000)" json:"samlAttributes"`
IsShared bool `json:"isShared"`
ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
@ -123,9 +124,9 @@ func GetApplicationCount(owner, field, value string) (int64, error) {
return session.Count(&Application{})
}
func GetOrganizationApplicationCount(owner, Organization, field, value string) (int64, error) {
func GetOrganizationApplicationCount(owner, organization, field, value string) (int64, error) {
session := GetSession(owner, -1, -1, field, value, "", "")
return session.Count(&Application{Organization: Organization})
return session.Where("organization = ? or is_shared = ? ", organization, true).Count(&Application{})
}
func GetApplications(owner string) ([]*Application, error) {
@ -140,7 +141,7 @@ func GetApplications(owner string) ([]*Application, error) {
func GetOrganizationApplications(owner string, organization string) ([]*Application, error) {
applications := []*Application{}
err := ormer.Engine.Desc("created_time").Find(&applications, &Application{Organization: organization})
err := ormer.Engine.Desc("created_time").Where("organization = ? or is_shared = ? ", organization, true).Find(&applications, &Application{})
if err != nil {
return applications, err
}
@ -162,7 +163,7 @@ func GetPaginationApplications(owner string, offset, limit int, field, value, so
func GetPaginationOrganizationApplications(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) ([]*Application, error) {
applications := []*Application{}
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
err := session.Find(&applications, &Application{Organization: organization})
err := session.Where("organization = ? or is_shared = ? ", organization, true).Find(&applications, &Application{})
if err != nil {
return applications, err
}
@ -337,12 +338,18 @@ func getApplication(owner string, name string) (*Application, error) {
return nil, nil
}
application := Application{Owner: owner, Name: name}
realApplicationName, sharedOrg := util.GetSharedOrgFromApp(name)
application := Application{Owner: owner, Name: realApplicationName}
existed, err := ormer.Engine.Get(&application)
if err != nil {
return nil, err
}
if application.IsShared && sharedOrg != "" {
application.Organization = sharedOrg
}
if existed {
err = extendApplicationWithProviders(&application)
if err != nil {
@ -428,11 +435,18 @@ func GetApplicationByUserId(userId string) (application *Application, err error)
func GetApplicationByClientId(clientId string) (*Application, error) {
application := Application{}
existed, err := ormer.Engine.Where("client_id=?", clientId).Get(&application)
realClientId, sharedOrg := util.GetSharedOrgFromApp(clientId)
existed, err := ormer.Engine.Where("client_id=?", realClientId).Get(&application)
if err != nil {
return nil, err
}
if application.IsShared && sharedOrg != "" {
application.Organization = sharedOrg
}
if existed {
err = extendApplicationWithProviders(&application)
if err != nil {
@ -626,6 +640,10 @@ func UpdateApplication(id string, application *Application) (bool, error) {
return false, err
}
if application.IsShared == true && application.Organization != "built-in" {
return false, fmt.Errorf("only applications belonging to built-in organization can be shared")
}
for _, providerItem := range application.Providers {
providerItem.Provider = nil
}

View File

@ -52,6 +52,9 @@ func GetFailedSigninConfigByUser(user *User) (int, int, error) {
if err != nil {
return 0, 0, err
}
if application == nil {
return 0, 0, fmt.Errorf("the application for user %s is not found", user.GetId())
}
failedSigninLimit := application.FailedSigninLimit
if failedSigninLimit == 0 {

View File

@ -319,6 +319,7 @@ func GetDefaultApplication(id string) (*Application, error) {
if defaultApplication == nil {
return nil, fmt.Errorf("The default application: %s does not exist", organization.DefaultApplication)
} else {
defaultApplication.Organization = organization.Name
return defaultApplication, nil
}
}

View File

@ -30,6 +30,13 @@ import (
var isCloudIntranet bool
const (
ProviderTypeGoogleCloudStorage = "Google Cloud Storage"
ProviderTypeTencentCloudCOS = "Tencent Cloud COS"
ProviderTypeAzureBlob = "Azure Blob"
ProviderTypeLocalFileSystem = "Local File System"
)
func init() {
isCloudIntranet = conf.GetConfigBool("isCloudIntranet")
}
@ -80,14 +87,14 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
host := ""
if provider.Type != "Local File System" {
if provider.Type != ProviderTypeLocalFileSystem {
// provider.Domain = "https://cdn.casbin.com/casdoor/"
host = util.GetUrlHost(provider.Domain)
} else {
// provider.Domain = "http://localhost:8000" or "https://door.casdoor.com"
host = util.UrlJoin(provider.Domain, "/files")
}
if provider.Type == "Azure Blob" {
if provider.Type == ProviderTypeAzureBlob || provider.Type == ProviderTypeGoogleCloudStorage {
host = util.UrlJoin(host, provider.Bucket)
}
@ -100,7 +107,7 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
fileUrl = fmt.Sprintf("%s?t=%s", fileUrl, util.GetCurrentUnixTime())
}
if provider.Type == "Tencent Cloud COS" {
if provider.Type == ProviderTypeTencentCloudCOS {
objectKey = escapePath(objectKey)
}
@ -135,11 +142,7 @@ func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffe
}
fileUrl, objectKey := GetUploadFileUrl(provider, fullFilePath, true)
objectKeyRefined := objectKey
if provider.Type == "Google Cloud Storage" {
objectKeyRefined = strings.TrimPrefix(objectKeyRefined, "/")
}
objectKeyRefined := refineObjectKey(provider, objectKey)
_, err = storageProvider.Put(objectKeyRefined, fileBuffer)
if err != nil {
@ -184,5 +187,13 @@ func DeleteFile(provider *Provider, objectKey string, lang string) error {
return err
}
return storageProvider.Delete(objectKey)
objectKeyRefined := refineObjectKey(provider, objectKey)
return storageProvider.Delete(objectKeyRefined)
}
func refineObjectKey(provider *Provider, objectKey string) string {
if provider.Type == ProviderTypeGoogleCloudStorage {
return strings.TrimPrefix(objectKey, "/")
}
return objectKey
}

View File

@ -277,7 +277,6 @@ func GetValidationBySaml(samlRequest string, host string) (string, string, error
if err != nil {
return "", "", err
}
if application == nil {
return "", "", fmt.Errorf("the application for user %s is not found", userId)
}

View File

@ -365,6 +365,10 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
},
}
if application.IsShared {
claims.Audience = []string{application.ClientId + "-org-" + user.Owner}
}
var token *jwt.Token
var refreshToken *jwt.Token

View File

@ -428,22 +428,26 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
if token == nil {
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: "authorization code is invalid",
ErrorDescription: fmt.Sprintf("authorization code: [%s] is invalid", code),
}, nil
}
if token.CodeIsUsed {
// anti replay attacks
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: "authorization code has been used",
ErrorDescription: fmt.Sprintf("authorization code has been used for token: [%s]", token.GetId()),
}, nil
}
if token.CodeChallenge != "" && pkceChallenge(verifier) != token.CodeChallenge {
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: "verifier is invalid",
}, nil
if token.CodeChallenge != "" {
challengeAnswer := pkceChallenge(verifier)
if challengeAnswer != token.CodeChallenge {
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: fmt.Sprintf("verifier is invalid, challengeAnswer: [%s], token.CodeChallenge: [%s]", challengeAnswer, token.CodeChallenge),
}, nil
}
}
if application.ClientSecret != clientSecret {
@ -452,13 +456,13 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
if token.CodeChallenge == "" {
return nil, &TokenError{
Error: InvalidClient,
ErrorDescription: "client_secret is invalid",
ErrorDescription: fmt.Sprintf("client_secret is invalid for application: [%s], token.CodeChallenge: empty", application.GetId()),
}, nil
} else {
if clientSecret != "" {
return nil, &TokenError{
Error: InvalidClient,
ErrorDescription: "client_secret is invalid",
ErrorDescription: fmt.Sprintf("client_secret is invalid for application: [%s], token.CodeChallenge: [%s]", application.GetId(), token.CodeChallenge),
}, nil
}
}
@ -467,15 +471,16 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
if application.Name != token.Application {
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: "the token is for wrong application (client_id)",
ErrorDescription: fmt.Sprintf("the token is for wrong application (client_id), application.Name: [%s], token.Application: [%s]", application.Name, token.Application),
}, nil
}
if time.Now().Unix() > token.CodeExpireIn {
nowUnix := time.Now().Unix()
if nowUnix > token.CodeExpireIn {
// code must be used within 5 minutes
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: "authorization code has expired",
ErrorDescription: fmt.Sprintf("authorization code has expired, nowUnix: [%s], token.CodeExpireIn: [%s]", time.Unix(nowUnix, 0).Format(time.RFC3339), time.Unix(token.CodeExpireIn, 0).Format(time.RFC3339)),
}, nil
}
return token, nil, nil

View File

@ -1138,7 +1138,7 @@ func (user *User) IsApplicationAdmin(application *Application) bool {
return false
}
return (user.Owner == application.Organization && user.IsAdmin) || user.IsGlobalAdmin()
return (user.Owner == application.Organization && user.IsAdmin) || user.IsGlobalAdmin() || (user.IsAdmin && application.IsShared)
}
func (user *User) IsGlobalAdmin() bool {

View File

@ -67,6 +67,17 @@ func AutoSigninFilter(ctx *context.Context) {
return
}
accessKey := ctx.Input.Query("accessKey")
accessSecret := ctx.Input.Query("accessSecret")
if accessKey != "" && accessSecret != "" {
userId, err := getUsernameByKeys(ctx)
if err != nil {
responseError(ctx, err.Error())
}
setSessionUser(ctx, userId)
}
// "/page?clientId=123&clientSecret=456"
userId, err := getUsernameByClientIdSecret(ctx)
if err != nil {

View File

@ -21,7 +21,6 @@ import (
"strings"
"github.com/beego/beego/context"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/object"
@ -126,7 +125,7 @@ func setSessionUser(ctx *context.Context, user string) {
}
// https://github.com/beego/beego/issues/3445#issuecomment-455411915
ctx.Input.CruSession.SessionReleaseIfPresent(ctx.ResponseWriter)
ctx.Input.CruSession.SessionRelease(ctx.ResponseWriter)
}
func setSessionExpire(ctx *context.Context, ExpireTime int64) {
@ -135,7 +134,7 @@ func setSessionExpire(ctx *context.Context, ExpireTime int64) {
if err != nil {
panic(err)
}
ctx.Input.CruSession.SessionReleaseIfPresent(ctx.ResponseWriter)
ctx.Input.CruSession.SessionRelease(ctx.ResponseWriter)
}
func setSessionOidc(ctx *context.Context, scope string, aud string) {
@ -147,7 +146,7 @@ func setSessionOidc(ctx *context.Context, scope string, aud string) {
if err != nil {
panic(err)
}
ctx.Input.CruSession.SessionReleaseIfPresent(ctx.ResponseWriter)
ctx.Input.CruSession.SessionRelease(ctx.ResponseWriter)
}
func parseBearerToken(ctx *context.Context) string {

View File

@ -154,6 +154,16 @@ func GetOwnerAndNameAndOtherFromId(id string) (string, string, string) {
return tokens[0], tokens[1], tokens[2]
}
func GetSharedOrgFromApp(rawName string) (name string, organization string) {
name = rawName
splitName := strings.Split(rawName, "-org-")
if len(splitName) >= 2 {
organization = splitName[len(splitName)-1]
name = splitName[0]
}
return name, organization
}
func GenerateId() string {
return uuid.NewString()
}

View File

@ -56,9 +56,11 @@ class AdapterListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -116,7 +116,6 @@ class ApplicationEditPage extends React.Component {
UNSAFE_componentWillMount() {
this.getApplication();
this.getOrganizations();
this.getProviders();
}
getApplication() {
@ -145,7 +144,9 @@ class ApplicationEditPage extends React.Component {
application: application,
});
this.getCerts(application.organization);
this.getProviders(application);
this.getCerts(application);
this.getSamlMetadata(application.enableSamlPostBinding);
});
@ -166,7 +167,11 @@ class ApplicationEditPage extends React.Component {
});
}
getCerts(owner) {
getCerts(application) {
let owner = application.organization;
if (application.isShared) {
owner = this.props.owner;
}
CertBackend.getCerts(owner)
.then((res) => {
this.setState({
@ -175,8 +180,12 @@ class ApplicationEditPage extends React.Component {
});
}
getProviders() {
ProviderBackend.getProviders(this.state.owner)
getProviders(application) {
let owner = application.organization;
if (application.isShared) {
owner = this.props.account.owner;
}
ProviderBackend.getProviders(owner)
.then((res) => {
if (res.status === "ok") {
this.setState({
@ -263,6 +272,16 @@ class ApplicationEditPage extends React.Component {
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Is shared"), i18next.t("general:Is shared - Tooltip"))} :
</Col>
<Col span={22} >
<Switch disabled={Setting.isAdminUser()} checked={this.state.application.isShared} onChange={checked => {
this.updateApplicationField("isShared", checked);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Logo"), i18next.t("general:Logo - Tooltip"))} :
@ -989,7 +1008,11 @@ class ApplicationEditPage extends React.Component {
redirectUri = "\"ERROR: You must specify at least one Redirect URL in 'Redirect URLs'\"";
}
const signInUrl = `/login/oauth/authorize?client_id=${this.state.application.clientId}&response_type=code&redirect_uri=${redirectUri}&scope=read&state=casdoor`;
let clientId = this.state.application.clientId;
if (this.state.application.isShared) {
clientId += `-org-${this.props.account.owner}`;
}
const signInUrl = `/login/oauth/authorize?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=read&state=casdoor`;
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "97%", width: "100%", background: "rgba(0,0,0,0.4)"};
if (!Setting.isPasswordEnabled(this.state.application)) {
signUpUrl = signInUrl.replace("/login/oauth/authorize", "/signup/oauth/authorize");

View File

@ -97,9 +97,11 @@ class ApplicationListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
@ -123,7 +125,7 @@ class ApplicationListPage extends BaseListPage {
render: (text, record, index) => {
return (
<Link to={`/applications/${record.organization}/${text}`}>
{text}
{Setting.getApplicationDisplayName(record)}
</Link>
);
},

View File

@ -73,9 +73,11 @@ class CertListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -55,9 +55,11 @@ class EnforcerListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -84,9 +84,11 @@ class GroupListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -68,9 +68,11 @@ class InvitationListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -72,9 +72,11 @@ class ModelListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -360,7 +360,7 @@ class OrganizationEditPage extends React.Component {
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.organization.defaultApplication} onChange={(value => {this.updateOrganizationField("defaultApplication", value);})}
options={this.state.applications?.map((item) => Setting.getOption(item.name, item.name))
options={this.state.applications?.map((item) => Setting.getOption(Setting.getApplicationDisplayName(item.name), item.name))
} />
</Col>
</Row>

View File

@ -115,11 +115,11 @@ class OrganizationListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
this.fetch({
pagination: {
...this.state.pagination,
total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
window.dispatchEvent(new Event("storageOrganizationsChanged"));
} else {

View File

@ -70,9 +70,11 @@ class PaymentListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -69,9 +69,11 @@ class PermissionListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -63,9 +63,11 @@ class PlanListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -59,9 +59,11 @@ class PricingListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -65,9 +65,11 @@ class ProductListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -906,7 +906,7 @@ class ProviderEditPage extends React.Component {
</Col>
</Row>
)}
{["Custom HTTP SMS", "Google Cloud Storage", "Qiniu Cloud Kodo", "Synology"].includes(this.state.provider.type) ? null : (
{["Custom HTTP SMS", "Qiniu Cloud Kodo", "Synology"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :

View File

@ -76,9 +76,11 @@ class ProviderListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -40,9 +40,11 @@ class ResourceListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -61,9 +61,11 @@ class RoleListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -27,9 +27,11 @@ class SessionListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -1371,6 +1371,13 @@ export function getApplicationName(application) {
return `${application?.owner}/${application?.name}`;
}
export function getApplicationDisplayName(application) {
if (application.isShared) {
return `${application.name}(Shared)`;
}
return application.name;
}
export function getRandomName() {
return Math.random().toString(36).slice(-6);
}

View File

@ -64,9 +64,11 @@ class SubscriptionListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -69,9 +69,11 @@ class SyncerListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -61,9 +61,11 @@ class TokenListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -125,7 +125,7 @@ class TransactionEditPage extends React.Component {
application: application,
});
this.getCerts(application.organization);
this.getCerts(application);
this.getSamlMetadata(application.enableSamlPostBinding);
});

View File

@ -54,9 +54,11 @@ class TransactionListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -110,9 +110,11 @@ class UserListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -61,9 +61,11 @@ class WebhookListPage extends BaseListPage {
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
this.fetch({
pagination: {
...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -24,6 +24,12 @@ import * as serviceWorker from "./serviceWorker";
import {BrowserRouter} from "react-router-dom";
import "./backend/FetchFilter";
if (!String.prototype.replaceAll) {
String.prototype.replaceAll = function(search, replace) {
return this.split(search).join(replace);
};
}
const container = document.getElementById("root");
const app = createRoot(container);