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 {