diff --git a/object/application.go b/object/application.go
index efc55aa3..3ea00ed3 100644
--- a/object/application.go
+++ b/object/application.go
@@ -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
}
diff --git a/object/organization.go b/object/organization.go
index ebd3f951..b1ab272c 100644
--- a/object/organization.go
+++ b/object/organization.go
@@ -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
}
}
diff --git a/object/user.go b/object/user.go
index 8be07ba8..7b7339be 100644
--- a/object/user.go
+++ b/object/user.go
@@ -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 {
diff --git a/util/string.go b/util/string.go
index 31c49fe9..add5b43d 100644
--- a/util/string.go
+++ b/util/string.go
@@ -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()
}
diff --git a/web/src/ApplicationEditPage.js b/web/src/ApplicationEditPage.js
index a8e2688d..cfe0ffc7 100644
--- a/web/src/ApplicationEditPage.js
+++ b/web/src/ApplicationEditPage.js
@@ -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 {
}} />
+
+
+ {Setting.getLabel(i18next.t("general:Is shared"), i18next.t("general:Is shared - Tooltip"))} :
+
+
+ {
+ this.updateApplicationField("isShared", checked);
+ }} />
+
+
{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 && this.props.account.owner !== "built-in") {
+ 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");
diff --git a/web/src/ApplicationListPage.js b/web/src/ApplicationListPage.js
index 33abe296..ba17bcf0 100644
--- a/web/src/ApplicationListPage.js
+++ b/web/src/ApplicationListPage.js
@@ -123,7 +123,7 @@ class ApplicationListPage extends BaseListPage {
render: (text, record, index) => {
return (
- {text}
+ {Setting.getApplicationDisplayName(record)}
);
},
diff --git a/web/src/OrganizationEditPage.js b/web/src/OrganizationEditPage.js
index 0589a6de..4dcc468c 100644
--- a/web/src/OrganizationEditPage.js
+++ b/web/src/OrganizationEditPage.js
@@ -360,7 +360,7 @@ class OrganizationEditPage extends React.Component {
diff --git a/web/src/Setting.js b/web/src/Setting.js
index 0a848ddc..d30aed8c 100644
--- a/web/src/Setting.js
+++ b/web/src/Setting.js
@@ -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);
}
diff --git a/web/src/TransactionEditPage.js b/web/src/TransactionEditPage.js
index 20f7a99a..4d594eaa 100644
--- a/web/src/TransactionEditPage.js
+++ b/web/src/TransactionEditPage.js
@@ -125,7 +125,7 @@ class TransactionEditPage extends React.Component {
application: application,
});
- this.getCerts(application.organization);
+ this.getCerts(application);
this.getSamlMetadata(application.enableSamlPostBinding);
});