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
This commit is contained in:
Yaodong Yu 2022-11-21 01:17:55 +08:00 committed by GitHub
parent 19ba37e0c2
commit 7e756b8ee2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 146 additions and 68 deletions

View File

@ -46,7 +46,7 @@ func (c *ApiController) GetApplications() {
if organization == "" { if organization == "" {
applications = object.GetApplications(owner) applications = object.GetApplications(owner)
} else { } else {
applications = object.GetApplicationsByOrganizationName(owner, organization) applications = object.GetOrganizationApplications(owner, organization)
} }
c.Data["json"] = object.GetMaskedApplications(applications, userId) c.Data["json"] = object.GetMaskedApplications(applications, userId)
@ -103,17 +103,31 @@ func (c *ApiController) GetUserApplication() {
// @router /get-organization-applications [get] // @router /get-organization-applications [get]
func (c *ApiController) GetOrganizationApplications() { func (c *ApiController) GetOrganizationApplications() {
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
owner := c.Input().Get("owner")
organization := c.Input().Get("organization") 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 == "" { if organization == "" {
c.ResponseError(c.T("ParameterErr.OrgMissingErr")) c.ResponseError(c.T("ParameterErr.OrgMissingErr"))
return return
} }
applications := object.GetApplicationsByOrganizationName(owner, organization) if limit == "" || page == "" {
c.Data["json"] = object.GetMaskedApplications(applications, userId) var applications []*object.Application
c.ServeJSON() 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 // UpdateApplication

View File

@ -307,7 +307,7 @@ func (c *ApiController) Login() {
} }
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", application.Organization)) 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) providerItem := application.GetProviderItem(provider.Name)
if !providerItem.IsProviderVisible() { if !providerItem.IsProviderVisible() {
c.ResponseError(fmt.Sprintf(c.T("ProviderErr.ProviderNotEnabled"), provider.Name)) c.ResponseError(fmt.Sprintf(c.T("ProviderErr.ProviderNotEnabled"), provider.Name))

View File

@ -81,7 +81,6 @@ func (c *ApiController) GetGlobalProviders() {
// @router /get-provider [get] // @router /get-provider [get]
func (c *ApiController) GetProvider() { func (c *ApiController) GetProvider() {
id := c.Input().Get("id") id := c.Input().Get("id")
c.Data["json"] = object.GetMaskedProvider(object.GetProvider(id)) c.Data["json"] = object.GetMaskedProvider(object.GetProvider(id))
c.ServeJSON() c.ServeJSON()
} }

View File

@ -60,7 +60,7 @@ func (c *ApiController) SendEmail() {
var provider *object.Provider var provider *object.Provider
if emailForm.Provider != "" { if emailForm.Provider != "" {
// called by frontend's TestEmailWidget, provider name is set by frontend // 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 { } 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 // 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 var ok bool

View File

@ -21,9 +21,10 @@ import (
"testing" "testing"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
) )
func TestDeployStaticFiles(t *testing.T) { func TestDeployStaticFiles(t *testing.T) {
provider := object.GetProvider("admin/provider_storage_aliyun_oss") provider := object.GetProvider(util.GetId("provider_storage_aliyun_oss"))
deployStaticFiles(provider) deployStaticFiles(provider)
} }

View File

@ -85,6 +85,16 @@ func GetApplicationCount(owner, field, value string) int {
return int(count) 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 { func GetApplications(owner string) []*Application {
applications := []*Application{} applications := []*Application{}
err := adapter.Engine.Desc("created_time").Find(&applications, &Application{Owner: owner}) err := adapter.Engine.Desc("created_time").Find(&applications, &Application{Owner: owner})
@ -95,8 +105,18 @@ func GetApplications(owner string) []*Application {
return applications return applications
} }
func GetPaginationApplications(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Application { func GetOrganizationApplications(owner string, organization string) []*Application {
applications := []*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) session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
err := session.Find(&applications) err := session.Find(&applications)
if err != nil { if err != nil {
@ -106,9 +126,10 @@ func GetPaginationApplications(owner string, offset, limit int, field, value, so
return applications 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{} 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 { if err != nil {
panic(err) panic(err)
} }
@ -132,7 +153,7 @@ func getProviderMap(owner string) map[string]*Provider {
} }
func extendApplicationWithProviders(application *Application) { func extendApplicationWithProviders(application *Application) {
m := getProviderMap(application.Owner) m := getProviderMap(application.Organization)
for _, providerItem := range application.Providers { for _, providerItem := range application.Providers {
if provider, ok := m[providerItem.Name]; ok { if provider, ok := m[providerItem.Name]; ok {
providerItem.Provider = provider providerItem.Provider = provider
@ -381,7 +402,7 @@ func IsAllowOrigin(origin string) bool {
} }
func getApplicationMap(organization string) map[string]*Application { func getApplicationMap(organization string) map[string]*Application {
applications := GetApplicationsByOrganizationName("admin", organization) applications := GetOrganizationApplications("admin", organization)
applicationMap := make(map[string]*Application) applicationMap := make(map[string]*Application)
for _, application := range applications { for _, application := range applications {

View File

@ -15,7 +15,7 @@
package object package object
func (application *Application) GetProviderByCategory(category string) *Provider { func (application *Application) GetProviderByCategory(category string) *Provider {
providers := GetProviders(application.Owner) providers := GetProviders(application.Organization)
m := map[string]*Provider{} m := map[string]*Provider{}
for _, provider := range providers { for _, provider := range providers {
if provider.Category != category { if provider.Category != category {

View File

@ -222,7 +222,7 @@ func initBuiltInLdap() {
} }
func initBuiltInProvider() { func initBuiltInProvider() {
provider := GetProvider("admin/provider_captcha_default") provider := GetProvider(util.GetId("provider_captcha_default"))
if provider != nil { if provider != nil {
return return
} }

View File

@ -168,7 +168,7 @@ func initDefinedLdap(ldap *Ldap) {
} }
func initDefinedProvider(provider *Provider) { func initDefinedProvider(provider *Provider) {
existed := GetProvider(provider.GetId()) existed := GetProvider(util.GetId(provider.Name))
if existed != nil { if existed != nil {
return return
} }

View File

@ -25,7 +25,7 @@ import (
type Provider struct { type Provider struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"` 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"` CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"` DisplayName string `xorm:"varchar(100)" json:"displayName"`
@ -93,8 +93,8 @@ func GetMaskedProviders(providers []*Provider) []*Provider {
} }
func GetProviderCount(owner, field, value string) int { func GetProviderCount(owner, field, value string) int {
session := GetSession(owner, -1, -1, field, value, "", "") session := GetSession("", -1, -1, field, value, "", "")
count, err := session.Count(&Provider{}) count, err := session.Where("owner = ? or owner = ? ", "admin", owner).Count(&Provider{})
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -114,7 +114,7 @@ func GetGlobalProviderCount(field, value string) int {
func GetProviders(owner string) []*Provider { func GetProviders(owner string) []*Provider {
providers := []*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 { if err != nil {
panic(err) panic(err)
} }
@ -133,9 +133,9 @@ func GetGlobalProviders() []*Provider {
} }
func GetPaginationProviders(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Provider { func GetPaginationProviders(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Provider {
var providers []*Provider providers := []*Provider{}
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder) session := GetSession("", offset, limit, field, value, sortField, sortOrder)
err := session.Find(&providers) err := session.Where("owner = ? or owner = ? ", "admin", owner).Find(&providers)
if err != nil { if err != nil {
panic(err) 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 { func GetPaginationGlobalProviders(offset, limit int, field, value, sortField, sortOrder string) []*Provider {
var providers []*Provider providers := []*Provider{}
session := GetSession("", offset, limit, field, value, sortField, sortOrder) session := GetSession("", offset, limit, field, value, sortField, sortOrder)
err := session.Find(&providers) err := session.Find(&providers)
if err != nil { if err != nil {
@ -159,7 +159,7 @@ func getProvider(owner string, name string) *Provider {
return nil return nil
} }
provider := Provider{Owner: owner, Name: name} provider := Provider{Name: name}
existed, err := adapter.Engine.Get(&provider) existed, err := adapter.Engine.Get(&provider)
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -15,7 +15,9 @@
package object package object
type ProviderItem struct { type ProviderItem struct {
Name string `json:"name"` Owner string `json:"owner"`
Name string `json:"name"`
CanSignUp bool `json:"canSignUp"` CanSignUp bool `json:"canSignUp"`
CanSignIn bool `json:"canSignIn"` CanSignIn bool `json:"canSignIn"`
CanUnlink bool `json:"canUnlink"` CanUnlink bool `json:"canUnlink"`

View File

@ -420,6 +420,8 @@ class App extends Component {
</Link> </Link>
</Menu.Item> </Menu.Item>
); );
}
if (Setting.isLocalAdminUser(this.state.account)) {
res.push( res.push(
<Menu.Item key="/applications"> <Menu.Item key="/applications">
<Link to="/applications"> <Link to="/applications">
@ -427,9 +429,6 @@ class App extends Component {
</Link> </Link>
</Menu.Item> </Menu.Item>
); );
}
if (Setting.isLocalAdminUser(this.state.account)) {
res.push( res.push(
<Menu.Item key="/providers"> <Menu.Item key="/providers">
<Link to="/providers"> <Link to="/providers">
@ -565,10 +564,9 @@ class App extends Component {
<Route exact path="/adapters" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterListPage account={this.state.account} {...props} />)} /> <Route exact path="/adapters" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterListPage account={this.state.account} {...props} />)} />
<Route exact path="/adapters/:organizationName/:adapterName" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterEditPage account={this.state.account} {...props} />)} /> <Route exact path="/adapters/:organizationName/:adapterName" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterEditPage account={this.state.account} {...props} />)} />
<Route exact path="/providers" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderListPage account={this.state.account} {...props} />)} /> <Route exact path="/providers" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderListPage account={this.state.account} {...props} />)} />
<Route exact path="/providers/:providerName" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderEditPage account={this.state.account} {...props} />)} />
<Route exact path="/providers/:organizationName/:providerName" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderEditPage account={this.state.account} {...props} />)} /> <Route exact path="/providers/:organizationName/:providerName" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderEditPage account={this.state.account} {...props} />)} />
<Route exact path="/applications" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationListPage account={this.state.account} {...props} />)} /> <Route exact path="/applications" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationListPage account={this.state.account} {...props} />)} />
<Route exact path="/applications/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationEditPage account={this.state.account} {...props} />)} /> <Route exact path="/applications/:organizationName/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationEditPage account={this.state.account} {...props} />)} />
<Route exact path="/resources" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceListPage account={this.state.account} {...props} />)} /> <Route exact path="/resources" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceListPage account={this.state.account} {...props} />)} />
{/* <Route exact path="/resources/:resourceName" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceEditPage account={this.state.account} {...props} />)}/>*/} {/* <Route exact path="/resources/:resourceName" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceEditPage account={this.state.account} {...props} />)}/>*/}
<Route exact path="/ldap/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapEditPage account={this.state.account} {...props} />)} /> <Route exact path="/ldap/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapEditPage account={this.state.account} {...props} />)} />

View File

@ -91,7 +91,7 @@ class ApplicationEditPage extends React.Component {
super(props); super(props);
this.state = { this.state = {
classes: props, classes: props,
owner: props.account.owner, owner: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
applicationName: props.match.params.applicationName, applicationName: props.match.params.applicationName,
application: null, application: null,
organizations: [], organizations: [],
@ -142,21 +142,11 @@ class ApplicationEditPage extends React.Component {
} }
getProviders() { getProviders() {
if (Setting.isAdminUser(this.props.account)) { ProviderBackend.getProviders(this.state.owner).then((res => {
ProviderBackend.getGlobalProviders() this.setState({
.then((res) => { providers: res,
this.setState({ });
providers: res, }));
});
});
} else {
ProviderBackend.getProviders(this.state.owner)
.then((res) => {
this.setState({
providers: res,
});
});
}
} }
getSamlMetadata() { getSamlMetadata() {
@ -287,7 +277,7 @@ class ApplicationEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} : {Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.application.organization} onChange={(value => {this.updateApplicationField("organization", value);})}> <Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account)} value={this.state.application.organization} onChange={(value => {this.updateApplicationField("organization", value);})}>
{ {
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>) this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>)
} }
@ -791,7 +781,7 @@ class ApplicationEditPage extends React.Component {
submitApplicationEdit(willExist) { submitApplicationEdit(willExist) {
const application = Setting.deepCopy(this.state.application); 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) => { .then((res) => {
if (res.msg === "") { if (res.msg === "") {
Setting.showMessage("success", "Successfully saved"); Setting.showMessage("success", "Successfully saved");
@ -802,7 +792,7 @@ class ApplicationEditPage extends React.Component {
if (willExist) { if (willExist) {
this.props.history.push("/applications"); this.props.history.push("/applications");
} else { } else {
this.props.history.push(`/applications/${this.state.application.name}`); this.props.history.push(`/applications/${this.state.application.organization}/${this.state.application.name}`);
} }
} else { } else {
Setting.showMessage("error", res.msg); Setting.showMessage("error", res.msg);

View File

@ -23,11 +23,28 @@ import i18next from "i18next";
import BaseListPage from "./BaseListPage"; import BaseListPage from "./BaseListPage";
class ApplicationListPage extends 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() { newApplication() {
const randomName = Setting.getRandomName(); const randomName = Setting.getRandomName();
return { return {
owner: "admin", // this.props.account.applicationname, owner: "admin", // this.props.account.applicationName,
name: `application_${randomName}`, name: `application_${randomName}`,
organization: this.state.organizationName,
createdTime: moment().format(), createdTime: moment().format(),
displayName: `New Application - ${randomName}`, displayName: `New Application - ${randomName}`,
logo: `${Setting.StaticBaseUrl}/img/casdoor-logo_1185x256.png`, logo: `${Setting.StaticBaseUrl}/img/casdoor-logo_1185x256.png`,
@ -61,7 +78,7 @@ class ApplicationListPage extends BaseListPage {
const newApplication = this.newApplication(); const newApplication = this.newApplication();
ApplicationBackend.addApplication(newApplication) ApplicationBackend.addApplication(newApplication)
.then((res) => { .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 => { .catch(error => {
@ -96,7 +113,7 @@ class ApplicationListPage extends BaseListPage {
...this.getColumnSearchProps("name"), ...this.getColumnSearchProps("name"),
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<Link to={`/applications/${text}`}> <Link to={`/applications/${record.organization}/${text}`}>
{text} {text}
</Link> </Link>
); );
@ -213,7 +230,7 @@ class ApplicationListPage extends BaseListPage {
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<div> <div>
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/applications/${record.name}`)}>{i18next.t("general:Edit")}</Button> <Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/applications/${record.organization}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
<Popconfirm <Popconfirm
title={`Sure to delete application: ${record.name} ?`} title={`Sure to delete application: ${record.name} ?`}
onConfirm={() => this.deleteApplication(index)} onConfirm={() => this.deleteApplication(index)}
@ -254,7 +271,8 @@ class ApplicationListPage extends BaseListPage {
const field = params.searchedColumn, value = params.searchText; const field = params.searchedColumn, value = params.searchText;
const sortField = params.sortField, sortOrder = params.sortOrder; const sortField = params.sortField, sortOrder = params.sortOrder;
this.setState({loading: true}); 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) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
this.setState({ this.setState({

View File

@ -22,6 +22,7 @@ import {authConfig} from "./auth/Auth";
import * as ProviderEditTestEmail from "./TestEmailWidget"; import * as ProviderEditTestEmail from "./TestEmailWidget";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import {CaptchaPreview} from "./common/CaptchaPreview"; import {CaptchaPreview} from "./common/CaptchaPreview";
import * as OrganizationBackend from "./backend/OrganizationBackend";
const {Option} = Select; const {Option} = Select;
const {TextArea} = Input; const {TextArea} = Input;
@ -34,11 +35,13 @@ class ProviderEditPage extends React.Component {
providerName: props.match.params.providerName, providerName: props.match.params.providerName,
owner: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName, owner: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
provider: null, provider: null,
organizations: [],
mode: props.location.mode !== undefined ? props.location.mode : "edit", mode: props.location.mode !== undefined ? props.location.mode : "edit",
}; };
} }
UNSAFE_componentWillMount() { UNSAFE_componentWillMount() {
this.getOrganizations();
this.getProvider(); 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) { parseProviderField(key, value) {
if (["port"].includes(key)) { if (["port"].includes(key)) {
value = Setting.myParseInt(value); value = Setting.myParseInt(value);
@ -191,6 +205,19 @@ class ProviderEditPage extends React.Component {
}} /> }} />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account)} value={this.state.provider.owner} onChange={(value => {this.updateProviderField("owner", value);})}>
{Setting.isAdminUser(this.props.account) ? <Option key={"admin"} value={"admin"}>{i18next.t("provider:admin (share)")}</Option> : null}
{
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>)
}
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:Category"), i18next.t("provider:Category - Tooltip"))} : {Setting.getLabel(i18next.t("provider:Category"), i18next.t("provider:Category - Tooltip"))} :
@ -760,18 +787,19 @@ class ProviderEditPage extends React.Component {
submitProviderEdit(willExist) { submitProviderEdit(willExist) {
const provider = Setting.deepCopy(this.state.provider); 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) => { .then((res) => {
if (res.msg === "") { if (res.msg === "") {
Setting.showMessage("success", "Successfully saved"); Setting.showMessage("success", "Successfully saved");
this.setState({ this.setState({
owner: this.state.provider.owner,
providerName: this.state.provider.name, providerName: this.state.provider.name,
}); });
if (willExist) { if (willExist) {
this.props.history.push("/providers"); this.props.history.push("/providers");
} else { } else {
this.props.history.push(`/providers/${this.state.provider.name}`); this.props.history.push(`/providers/${this.state.provider.owner}/${this.state.provider.name}`);
} }
} else { } else {
Setting.showMessage("error", res.msg); Setting.showMessage("error", res.msg);

View File

@ -27,7 +27,7 @@ class ProviderListPage extends BaseListPage {
super(props); super(props);
this.state = { this.state = {
classes: props, classes: props,
owner: Setting.isAdminUser(props.account) ? "admin" : props.account.organization.name, owner: Setting.isAdminUser(props.account) ? "admin" : props.account.owner,
data: [], data: [],
pagination: { pagination: {
current: 1, current: 1,
@ -96,12 +96,20 @@ class ProviderListPage extends BaseListPage {
...this.getColumnSearchProps("name"), ...this.getColumnSearchProps("name"),
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<Link to={`/providers/${text}`}> <Link to={`/providers/${record.owner}/${text}`}>
{text} {text}
</Link> </Link>
); );
}, },
}, },
{
title: i18next.t("general:Organization"),
dataIndex: "owner",
key: "owner",
width: "150px",
sorter: true,
...this.getColumnSearchProps("owner"),
},
{ {
title: i18next.t("general:Created time"), title: i18next.t("general:Created time"),
dataIndex: "createdTime", dataIndex: "createdTime",
@ -192,12 +200,12 @@ class ProviderListPage extends BaseListPage {
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<div> <div>
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/providers/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button> <Button disabled={!Setting.isAdminUser(this.props.account) && (record.owner !== this.props.account.owner)} style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/providers/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
<Popconfirm <Popconfirm
title={`Sure to delete provider: ${record.name} ?`} title={`Sure to delete provider: ${record.name} ?`}
onConfirm={() => this.deleteProvider(index)} onConfirm={() => this.deleteProvider(index)}
> >
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button> <Button disabled={!Setting.isAdminUser(this.props.account) && (record.owner !== this.props.account.owner)} style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
</Popconfirm> </Popconfirm>
</div> </div>
); );

View File

@ -95,7 +95,7 @@ class ResourceListPage extends BaseListPage {
...this.getColumnSearchProps("provider"), ...this.getColumnSearchProps("provider"),
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<Link to={`/providers/${text}`}> <Link to={`/providers/${record.owner}/${text}`}>
{text} {text}
</Link> </Link>
); );

View File

@ -102,7 +102,7 @@ class TokenListPage extends BaseListPage {
...this.getColumnSearchProps("application"), ...this.getColumnSearchProps("application"),
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<Link to={`/applications/${text}`}> <Link to={`/applications/${record.organization}/${text}`}>
{text} {text}
</Link> </Link>
); );

View File

@ -167,7 +167,7 @@ class UserListPage extends BaseListPage {
...this.getColumnSearchProps("signupApplication"), ...this.getColumnSearchProps("signupApplication"),
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<Link to={`/applications/${text}`}> <Link to={`/applications/${record.owner}/${text}`}>
{text} {text}
</Link> </Link>
); );

View File

@ -24,8 +24,8 @@ export function getApplications(owner, page = "", pageSize = "", field = "", val
}).then(res => res.json()); }).then(res => res.json());
} }
export function getApplicationsByOrganization(owner, organization) { export function getApplicationsByOrganization(owner, organization, page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "") {
return fetch(`${Setting.ServerUrl}/api/get-organization-applications?owner=${owner}&organization=${organization}`, { 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", method: "GET",
credentials: "include", credentials: "include",
headers: { headers: {
@ -68,7 +68,6 @@ export function updateApplication(owner, name, application) {
export function addApplication(application) { export function addApplication(application) {
const newApplication = Setting.deepCopy(application); const newApplication = Setting.deepCopy(application);
newApplication.organization = "built-in";
return fetch(`${Setting.ServerUrl}/api/add-application`, { return fetch(`${Setting.ServerUrl}/api/add-application`, {
method: "POST", method: "POST",
credentials: "include", credentials: "include",