From 7e756b8ee2a7e6b6a899d7aa0c9bfa8e6c6b20b8 Mon Sep 17 00:00:00 2001 From: Yaodong Yu <2814461814@qq.com> Date: Mon, 21 Nov 2022 01:17:55 +0800 Subject: [PATCH] feat: manager applications in organization scope (#1290) * feat: manager applications in organization scope(front end) * fix: application can use own organization and admin provider * fix: improve methed to get provider * fix: modify provider methods by convention --- controllers/application.go | 24 +++++++++++++++----- controllers/auth.go | 2 +- controllers/provider.go | 1 - controllers/service.go | 2 +- deployment/deploy_test.go | 3 ++- object/application.go | 31 +++++++++++++++++++++----- object/application_item.go | 2 +- object/init.go | 2 +- object/init_data.go | 2 +- object/provider.go | 18 +++++++-------- object/provider_item.go | 4 +++- web/src/App.js | 8 +++---- web/src/ApplicationEditPage.js | 28 ++++++++--------------- web/src/ApplicationListPage.js | 28 ++++++++++++++++++----- web/src/ProviderEditPage.js | 32 +++++++++++++++++++++++++-- web/src/ProviderListPage.js | 16 ++++++++++---- web/src/ResourceListPage.js | 2 +- web/src/TokenListPage.js | 2 +- web/src/UserListPage.js | 2 +- web/src/backend/ApplicationBackend.js | 5 ++--- 20 files changed, 146 insertions(+), 68 deletions(-) diff --git a/controllers/application.go b/controllers/application.go index 37a9a949..8f074d5a 100644 --- a/controllers/application.go +++ b/controllers/application.go @@ -46,7 +46,7 @@ func (c *ApiController) GetApplications() { if organization == "" { applications = object.GetApplications(owner) } else { - applications = object.GetApplicationsByOrganizationName(owner, organization) + applications = object.GetOrganizationApplications(owner, organization) } c.Data["json"] = object.GetMaskedApplications(applications, userId) @@ -103,17 +103,31 @@ func (c *ApiController) GetUserApplication() { // @router /get-organization-applications [get] func (c *ApiController) GetOrganizationApplications() { userId := c.GetSessionUsername() - owner := c.Input().Get("owner") organization := c.Input().Get("organization") + owner := c.Input().Get("owner") + limit := c.Input().Get("pageSize") + page := c.Input().Get("p") + field := c.Input().Get("field") + value := c.Input().Get("value") + sortField := c.Input().Get("sortField") + sortOrder := c.Input().Get("sortOrder") if organization == "" { c.ResponseError(c.T("ParameterErr.OrgMissingErr")) return } - applications := object.GetApplicationsByOrganizationName(owner, organization) - c.Data["json"] = object.GetMaskedApplications(applications, userId) - c.ServeJSON() + if limit == "" || page == "" { + var applications []*object.Application + applications = object.GetOrganizationApplications(owner, organization) + c.Data["json"] = object.GetMaskedApplications(applications, userId) + c.ServeJSON() + } else { + limit := util.ParseInt(limit) + paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetOrganizationApplicationCount(owner, organization, field, value))) + applications := object.GetMaskedApplications(object.GetPaginationOrganizationApplications(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder), userId) + c.ResponseOk(applications, paginator.Nums()) + } } // UpdateApplication diff --git a/controllers/auth.go b/controllers/auth.go index 9712be36..ac699f49 100644 --- a/controllers/auth.go +++ b/controllers/auth.go @@ -307,7 +307,7 @@ func (c *ApiController) Login() { } organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", application.Organization)) - provider := object.GetProvider(fmt.Sprintf("admin/%s", form.Provider)) + provider := object.GetProvider(util.GetId(form.Provider)) providerItem := application.GetProviderItem(provider.Name) if !providerItem.IsProviderVisible() { c.ResponseError(fmt.Sprintf(c.T("ProviderErr.ProviderNotEnabled"), provider.Name)) diff --git a/controllers/provider.go b/controllers/provider.go index a6a1636f..5b14fe89 100644 --- a/controllers/provider.go +++ b/controllers/provider.go @@ -81,7 +81,6 @@ func (c *ApiController) GetGlobalProviders() { // @router /get-provider [get] func (c *ApiController) GetProvider() { id := c.Input().Get("id") - c.Data["json"] = object.GetMaskedProvider(object.GetProvider(id)) c.ServeJSON() } diff --git a/controllers/service.go b/controllers/service.go index 33d09cf9..a58edf4d 100644 --- a/controllers/service.go +++ b/controllers/service.go @@ -60,7 +60,7 @@ func (c *ApiController) SendEmail() { var provider *object.Provider if emailForm.Provider != "" { // called by frontend's TestEmailWidget, provider name is set by frontend - provider = object.GetProvider(fmt.Sprintf("admin/%s", emailForm.Provider)) + provider = object.GetProvider(util.GetId(emailForm.Provider)) } else { // called by Casdoor SDK via Client ID & Client Secret, so the used Email provider will be the application' Email provider or the default Email provider var ok bool diff --git a/deployment/deploy_test.go b/deployment/deploy_test.go index 37773c84..7d76c5a1 100644 --- a/deployment/deploy_test.go +++ b/deployment/deploy_test.go @@ -21,9 +21,10 @@ import ( "testing" "github.com/casdoor/casdoor/object" + "github.com/casdoor/casdoor/util" ) func TestDeployStaticFiles(t *testing.T) { - provider := object.GetProvider("admin/provider_storage_aliyun_oss") + provider := object.GetProvider(util.GetId("provider_storage_aliyun_oss")) deployStaticFiles(provider) } diff --git a/object/application.go b/object/application.go index 2804bf3b..16ab1a21 100644 --- a/object/application.go +++ b/object/application.go @@ -85,6 +85,16 @@ func GetApplicationCount(owner, field, value string) int { return int(count) } +func GetOrganizationApplicationCount(owner, Organization, field, value string) int { + session := GetSession(owner, -1, -1, field, value, "", "") + count, err := session.Count(&Application{Organization: Organization}) + if err != nil { + panic(err) + } + + return int(count) +} + func GetApplications(owner string) []*Application { applications := []*Application{} err := adapter.Engine.Desc("created_time").Find(&applications, &Application{Owner: owner}) @@ -95,8 +105,18 @@ func GetApplications(owner string) []*Application { return applications } -func GetPaginationApplications(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Application { +func GetOrganizationApplications(owner string, organization string) []*Application { applications := []*Application{} + err := adapter.Engine.Desc("created_time").Find(&applications, &Application{Owner: owner, Organization: organization}) + if err != nil { + panic(err) + } + + return applications +} + +func GetPaginationApplications(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Application { + var applications []*Application session := GetSession(owner, offset, limit, field, value, sortField, sortOrder) err := session.Find(&applications) if err != nil { @@ -106,9 +126,10 @@ func GetPaginationApplications(owner string, offset, limit int, field, value, so return applications } -func GetApplicationsByOrganizationName(owner string, organization string) []*Application { +func GetPaginationOrganizationApplications(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) []*Application { applications := []*Application{} - err := adapter.Engine.Desc("created_time").Find(&applications, &Application{Owner: owner, Organization: organization}) + session := GetSession(owner, offset, limit, field, value, sortField, sortOrder) + err := session.Find(&applications, &Application{Owner: owner, Organization: organization}) if err != nil { panic(err) } @@ -132,7 +153,7 @@ func getProviderMap(owner string) map[string]*Provider { } func extendApplicationWithProviders(application *Application) { - m := getProviderMap(application.Owner) + m := getProviderMap(application.Organization) for _, providerItem := range application.Providers { if provider, ok := m[providerItem.Name]; ok { providerItem.Provider = provider @@ -381,7 +402,7 @@ func IsAllowOrigin(origin string) bool { } func getApplicationMap(organization string) map[string]*Application { - applications := GetApplicationsByOrganizationName("admin", organization) + applications := GetOrganizationApplications("admin", organization) applicationMap := make(map[string]*Application) for _, application := range applications { diff --git a/object/application_item.go b/object/application_item.go index 63efe025..47dd93a8 100644 --- a/object/application_item.go +++ b/object/application_item.go @@ -15,7 +15,7 @@ package object func (application *Application) GetProviderByCategory(category string) *Provider { - providers := GetProviders(application.Owner) + providers := GetProviders(application.Organization) m := map[string]*Provider{} for _, provider := range providers { if provider.Category != category { diff --git a/object/init.go b/object/init.go index cc2a4868..f5f2964f 100644 --- a/object/init.go +++ b/object/init.go @@ -222,7 +222,7 @@ func initBuiltInLdap() { } func initBuiltInProvider() { - provider := GetProvider("admin/provider_captcha_default") + provider := GetProvider(util.GetId("provider_captcha_default")) if provider != nil { return } diff --git a/object/init_data.go b/object/init_data.go index 6eb16793..323dbc6e 100644 --- a/object/init_data.go +++ b/object/init_data.go @@ -168,7 +168,7 @@ func initDefinedLdap(ldap *Ldap) { } func initDefinedProvider(provider *Provider) { - existed := GetProvider(provider.GetId()) + existed := GetProvider(util.GetId(provider.Name)) if existed != nil { return } diff --git a/object/provider.go b/object/provider.go index 5da731a0..df18ebb2 100644 --- a/object/provider.go +++ b/object/provider.go @@ -25,7 +25,7 @@ import ( type Provider struct { Owner string `xorm:"varchar(100) notnull pk" json:"owner"` - Name string `xorm:"varchar(100) notnull pk" json:"name"` + Name string `xorm:"varchar(100) notnull pk unique" json:"name"` CreatedTime string `xorm:"varchar(100)" json:"createdTime"` DisplayName string `xorm:"varchar(100)" json:"displayName"` @@ -93,8 +93,8 @@ func GetMaskedProviders(providers []*Provider) []*Provider { } func GetProviderCount(owner, field, value string) int { - session := GetSession(owner, -1, -1, field, value, "", "") - count, err := session.Count(&Provider{}) + session := GetSession("", -1, -1, field, value, "", "") + count, err := session.Where("owner = ? or owner = ? ", "admin", owner).Count(&Provider{}) if err != nil { panic(err) } @@ -114,7 +114,7 @@ func GetGlobalProviderCount(field, value string) int { func GetProviders(owner string) []*Provider { providers := []*Provider{} - err := adapter.Engine.Desc("created_time").Find(&providers, &Provider{Owner: owner}) + err := adapter.Engine.Where("owner = ? or owner = ? ", "admin", owner).Desc("created_time").Find(&providers, &Provider{}) if err != nil { panic(err) } @@ -133,9 +133,9 @@ func GetGlobalProviders() []*Provider { } func GetPaginationProviders(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Provider { - var providers []*Provider - session := GetSession(owner, offset, limit, field, value, sortField, sortOrder) - err := session.Find(&providers) + providers := []*Provider{} + session := GetSession("", offset, limit, field, value, sortField, sortOrder) + err := session.Where("owner = ? or owner = ? ", "admin", owner).Find(&providers) if err != nil { panic(err) } @@ -144,7 +144,7 @@ func GetPaginationProviders(owner string, offset, limit int, field, value, sortF } func GetPaginationGlobalProviders(offset, limit int, field, value, sortField, sortOrder string) []*Provider { - var providers []*Provider + providers := []*Provider{} session := GetSession("", offset, limit, field, value, sortField, sortOrder) err := session.Find(&providers) if err != nil { @@ -159,7 +159,7 @@ func getProvider(owner string, name string) *Provider { return nil } - provider := Provider{Owner: owner, Name: name} + provider := Provider{Name: name} existed, err := adapter.Engine.Get(&provider) if err != nil { panic(err) diff --git a/object/provider_item.go b/object/provider_item.go index 00083da2..28586b73 100644 --- a/object/provider_item.go +++ b/object/provider_item.go @@ -15,7 +15,9 @@ package object type ProviderItem struct { - Name string `json:"name"` + Owner string `json:"owner"` + Name string `json:"name"` + CanSignUp bool `json:"canSignUp"` CanSignIn bool `json:"canSignIn"` CanUnlink bool `json:"canUnlink"` diff --git a/web/src/App.js b/web/src/App.js index f9b67887..10b83e62 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -420,6 +420,8 @@ class App extends Component { ); + } + if (Setting.isLocalAdminUser(this.state.account)) { res.push( @@ -427,9 +429,6 @@ class App extends Component { ); - } - - if (Setting.isLocalAdminUser(this.state.account)) { res.push( @@ -565,10 +564,9 @@ class App extends Component { this.renderLoginIfNotLoggedIn()} /> this.renderLoginIfNotLoggedIn()} /> this.renderLoginIfNotLoggedIn()} /> - this.renderLoginIfNotLoggedIn()} /> this.renderLoginIfNotLoggedIn()} /> this.renderLoginIfNotLoggedIn()} /> - this.renderLoginIfNotLoggedIn()} /> + this.renderLoginIfNotLoggedIn()} /> this.renderLoginIfNotLoggedIn()} /> {/* this.renderLoginIfNotLoggedIn()}/>*/} this.renderLoginIfNotLoggedIn()} /> diff --git a/web/src/ApplicationEditPage.js b/web/src/ApplicationEditPage.js index c8db477a..c4c09524 100644 --- a/web/src/ApplicationEditPage.js +++ b/web/src/ApplicationEditPage.js @@ -91,7 +91,7 @@ class ApplicationEditPage extends React.Component { super(props); this.state = { classes: props, - owner: props.account.owner, + owner: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName, applicationName: props.match.params.applicationName, application: null, organizations: [], @@ -142,21 +142,11 @@ class ApplicationEditPage extends React.Component { } getProviders() { - if (Setting.isAdminUser(this.props.account)) { - ProviderBackend.getGlobalProviders() - .then((res) => { - this.setState({ - providers: res, - }); - }); - } else { - ProviderBackend.getProviders(this.state.owner) - .then((res) => { - this.setState({ - providers: res, - }); - }); - } + ProviderBackend.getProviders(this.state.owner).then((res => { + this.setState({ + providers: res, + }); + })); } getSamlMetadata() { @@ -287,7 +277,7 @@ class ApplicationEditPage extends React.Component { {Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} : - {this.updateApplicationField("organization", value);})}> { this.state.organizations.map((organization, index) => ) } @@ -791,7 +781,7 @@ class ApplicationEditPage extends React.Component { submitApplicationEdit(willExist) { const application = Setting.deepCopy(this.state.application); - ApplicationBackend.updateApplication(this.state.application.owner, this.state.applicationName, application) + ApplicationBackend.updateApplication("admin", this.state.applicationName, application) .then((res) => { if (res.msg === "") { Setting.showMessage("success", "Successfully saved"); @@ -802,7 +792,7 @@ class ApplicationEditPage extends React.Component { if (willExist) { this.props.history.push("/applications"); } else { - this.props.history.push(`/applications/${this.state.application.name}`); + this.props.history.push(`/applications/${this.state.application.organization}/${this.state.application.name}`); } } else { Setting.showMessage("error", res.msg); diff --git a/web/src/ApplicationListPage.js b/web/src/ApplicationListPage.js index f4417ea8..9ec3e738 100644 --- a/web/src/ApplicationListPage.js +++ b/web/src/ApplicationListPage.js @@ -23,11 +23,28 @@ import i18next from "i18next"; import BaseListPage from "./BaseListPage"; class ApplicationListPage extends BaseListPage { + constructor(props) { + super(props); + this.state = { + classes: props, + organizationName: props.account.owner, + data: [], + pagination: { + current: 1, + pageSize: 10, + }, + loading: false, + searchText: "", + searchedColumn: "", + }; + } + newApplication() { const randomName = Setting.getRandomName(); return { - owner: "admin", // this.props.account.applicationname, + owner: "admin", // this.props.account.applicationName, name: `application_${randomName}`, + organization: this.state.organizationName, createdTime: moment().format(), displayName: `New Application - ${randomName}`, logo: `${Setting.StaticBaseUrl}/img/casdoor-logo_1185x256.png`, @@ -61,7 +78,7 @@ class ApplicationListPage extends BaseListPage { const newApplication = this.newApplication(); ApplicationBackend.addApplication(newApplication) .then((res) => { - this.props.history.push({pathname: `/applications/${newApplication.name}`, mode: "add"}); + this.props.history.push({pathname: `/applications/${newApplication.organization}/${newApplication.name}`, mode: "add"}); } ) .catch(error => { @@ -96,7 +113,7 @@ class ApplicationListPage extends BaseListPage { ...this.getColumnSearchProps("name"), render: (text, record, index) => { return ( - + {text} ); @@ -213,7 +230,7 @@ class ApplicationListPage extends BaseListPage { render: (text, record, index) => { return (
- + this.deleteApplication(index)} @@ -254,7 +271,8 @@ class ApplicationListPage extends BaseListPage { const field = params.searchedColumn, value = params.searchText; const sortField = params.sortField, sortOrder = params.sortOrder; this.setState({loading: true}); - ApplicationBackend.getApplications("admin", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder) + (Setting.isAdminUser(this.props.account) ? ApplicationBackend.getApplications("admin", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder) : + ApplicationBackend.getApplicationsByOrganization("admin", this.state.organizationName, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)) .then((res) => { if (res.status === "ok") { this.setState({ diff --git a/web/src/ProviderEditPage.js b/web/src/ProviderEditPage.js index 8070e867..f9f842b8 100644 --- a/web/src/ProviderEditPage.js +++ b/web/src/ProviderEditPage.js @@ -22,6 +22,7 @@ import {authConfig} from "./auth/Auth"; import * as ProviderEditTestEmail from "./TestEmailWidget"; import copy from "copy-to-clipboard"; import {CaptchaPreview} from "./common/CaptchaPreview"; +import * as OrganizationBackend from "./backend/OrganizationBackend"; const {Option} = Select; const {TextArea} = Input; @@ -34,11 +35,13 @@ class ProviderEditPage extends React.Component { providerName: props.match.params.providerName, owner: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName, provider: null, + organizations: [], mode: props.location.mode !== undefined ? props.location.mode : "edit", }; } UNSAFE_componentWillMount() { + this.getOrganizations(); this.getProvider(); } @@ -51,6 +54,17 @@ class ProviderEditPage extends React.Component { }); } + getOrganizations() { + if (Setting.isAdminUser(this.props.account)) { + OrganizationBackend.getOrganizations("admin") + .then((res) => { + this.setState({ + organizations: res.msg === undefined ? res : [], + }); + }); + } + } + parseProviderField(key, value) { if (["port"].includes(key)) { value = Setting.myParseInt(value); @@ -191,6 +205,19 @@ class ProviderEditPage extends React.Component { }} /> + + + {Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} : + + + + + {Setting.getLabel(i18next.t("provider:Category"), i18next.t("provider:Category - Tooltip"))} : @@ -760,18 +787,19 @@ class ProviderEditPage extends React.Component { submitProviderEdit(willExist) { const provider = Setting.deepCopy(this.state.provider); - ProviderBackend.updateProvider(this.state.provider.owner, this.state.providerName, provider) + ProviderBackend.updateProvider(this.state.owner, this.state.providerName, provider) .then((res) => { if (res.msg === "") { Setting.showMessage("success", "Successfully saved"); this.setState({ + owner: this.state.provider.owner, providerName: this.state.provider.name, }); if (willExist) { this.props.history.push("/providers"); } else { - this.props.history.push(`/providers/${this.state.provider.name}`); + this.props.history.push(`/providers/${this.state.provider.owner}/${this.state.provider.name}`); } } else { Setting.showMessage("error", res.msg); diff --git a/web/src/ProviderListPage.js b/web/src/ProviderListPage.js index f9ddf74e..6b99778f 100644 --- a/web/src/ProviderListPage.js +++ b/web/src/ProviderListPage.js @@ -27,7 +27,7 @@ class ProviderListPage extends BaseListPage { super(props); this.state = { classes: props, - owner: Setting.isAdminUser(props.account) ? "admin" : props.account.organization.name, + owner: Setting.isAdminUser(props.account) ? "admin" : props.account.owner, data: [], pagination: { current: 1, @@ -96,12 +96,20 @@ class ProviderListPage extends BaseListPage { ...this.getColumnSearchProps("name"), render: (text, record, index) => { return ( - + {text} ); }, }, + { + title: i18next.t("general:Organization"), + dataIndex: "owner", + key: "owner", + width: "150px", + sorter: true, + ...this.getColumnSearchProps("owner"), + }, { title: i18next.t("general:Created time"), dataIndex: "createdTime", @@ -192,12 +200,12 @@ class ProviderListPage extends BaseListPage { render: (text, record, index) => { return (
- + this.deleteProvider(index)} > - +
); diff --git a/web/src/ResourceListPage.js b/web/src/ResourceListPage.js index c774f490..b980af7f 100644 --- a/web/src/ResourceListPage.js +++ b/web/src/ResourceListPage.js @@ -95,7 +95,7 @@ class ResourceListPage extends BaseListPage { ...this.getColumnSearchProps("provider"), render: (text, record, index) => { return ( - + {text} ); diff --git a/web/src/TokenListPage.js b/web/src/TokenListPage.js index a87ed806..4e1b10fa 100644 --- a/web/src/TokenListPage.js +++ b/web/src/TokenListPage.js @@ -102,7 +102,7 @@ class TokenListPage extends BaseListPage { ...this.getColumnSearchProps("application"), render: (text, record, index) => { return ( - + {text} ); diff --git a/web/src/UserListPage.js b/web/src/UserListPage.js index 42f4f332..8a10771b 100644 --- a/web/src/UserListPage.js +++ b/web/src/UserListPage.js @@ -167,7 +167,7 @@ class UserListPage extends BaseListPage { ...this.getColumnSearchProps("signupApplication"), render: (text, record, index) => { return ( - + {text} ); diff --git a/web/src/backend/ApplicationBackend.js b/web/src/backend/ApplicationBackend.js index 57a45fb8..6a7f5343 100644 --- a/web/src/backend/ApplicationBackend.js +++ b/web/src/backend/ApplicationBackend.js @@ -24,8 +24,8 @@ export function getApplications(owner, page = "", pageSize = "", field = "", val }).then(res => res.json()); } -export function getApplicationsByOrganization(owner, organization) { - return fetch(`${Setting.ServerUrl}/api/get-organization-applications?owner=${owner}&organization=${organization}`, { +export function getApplicationsByOrganization(owner, organization, page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "") { + return fetch(`${Setting.ServerUrl}/api/get-organization-applications?owner=${owner}&organization=${organization}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { method: "GET", credentials: "include", headers: { @@ -68,7 +68,6 @@ export function updateApplication(owner, name, application) { export function addApplication(application) { const newApplication = Setting.deepCopy(application); - newApplication.organization = "built-in"; return fetch(`${Setting.ServerUrl}/api/add-application`, { method: "POST", credentials: "include",